∇
Chapter 24 · Section 24.2 · 95 min

Function calling and structured outputs

Function calling is how a language model asks a program to do something for it. The model writes a short piece of structured text, such as {"name": "get_forecast", "arguments": {"city": "Oslo", "days": 3}}. The program around the model reads that text, runs the function, and hands the result back. A person forgives a missing comma, but a program does not. So every agent needs a precise contract for what a call looks like, a way to check each call against it, and ideally a way to make broken calls impossible to write.

Here is why this matters, in numbers. An agent, as §24.1 set it up, runs a loop. The model writes text, and a small program called a parser reads an action out of it, either a tool call or a final answer. The tool runs, and its result is added to the model's context for the next turn. When the parser cannot read the text, it returns a special symbol ⊥\bot, meaning "malformed". Suppose a model writes a malformed call once in every 50, and a task needs 30 calls. Then at least one call of the task is broken with probability 1−0.9830=0.451 - 0.98^{30} = 0.45. Almost half of all tasks hit a broken step before any reasoning has gone wrong.

There are three lines of defense, and this section builds all three.

  1. State the contract. Describe each tool's arguments exactly, in a standard language called JSON Schema, and train the model on calls that follow it.
  2. Check and repair. Check each call after it is written, and send the list of what is wrong back to the model so it can fix the call.
  3. Make malformed output impossible. While the model writes, remove every token that would break the contract before the next token is chosen. This is called constrained decoding.

The third defense gets the most space, because it is an exact algorithm with a proof. It is built on finite automata, small machines that read text one character at a time, and for nested formats like JSON on automata that also keep a stack. It has a cost that most practitioners never notice, because it changes what the model says, and we will measure by how much. The section ends with how models are trained to call tools and how that skill is measured.

A tool is a function with a contract

A tool is a function that the model may ask the surrounding program to run. The model never runs anything itself. It writes a request, and the program decides what to do with it. To write a good request, the model needs the function's name, a description of what it does, and an exact description of its arguments.

Here is a tool definition as you would send it to a model provider. It is an ordinary function signature written in JSON. The description tells the model when to use the tool, and the schema says which arguments are acceptable.

{
  "name": "place_order",
  "description": "Place an order for in-stock items. Quantities are whole numbers from 1 to 99.",
  "input_schema": {
    "type": "object",
    "properties": {
      "customer_id": {"type": "string"},
      "items": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "sku": {"type": "string"},
            "qty": {"type": "integer", "minimum": 1, "maximum": 99}
          },
          "required": ["sku", "qty"],
          "additionalProperties": false
        }
      },
      "shipping": {"enum": ["standard", "express"]},
      "gift_note": {"anyOf": [{"type": "string"}, {"type": "null"}]}
    },
    "required": ["customer_id", "items", "shipping"],
    "additionalProperties": false
  }
}

Read it as a function signature. customer_id is a required string. items is a required list of objects, and each object has a string sku and a whole-number qty from 1 to 99, and nothing else. shipping must be one of two words. gift_note may be left out, and when present it is a string or null. No other argument is allowed.

The schema is written in JSON Schema, a standard language for saying which JSON values are acceptable (JSON Schema 2020-12, the current version). Anthropic's API puts the schema in a field called input_schema, and OpenAI's in one called parameters. Open-weight models receive the same object written into their prompt. JSON Schema is large, with string patterns, conditional rules, and references between schemas. Tool definitions use a small core of it. That core is worth knowing exactly, because the exact rules are where implementations disagree.

In plain words, a schema is a list of independent checks, and a value is valid when it passes all of them. Each check speaks only about values of its own type. That last rule is why real schemas pair every keyword with a type. On its own, {"minimum": 1} would let a string through.

Two of these rules collide with Python, and both cause real bugs. JSON has only one kind of number, so json.loads turns 3.0 into a Python float even though JSON Schema calls it an integer, and it turns 1e2 into the float 100.0. And Python's bool is a subclass of int, so isinstance(True, int) is True. A validator written with isinstance therefore accepts true as a quantity without complaint. The cell shows both.

Python · cell 1

The check never converts a Python int to a float. Python integers have no size limit, so json.loads turns a 401-digit number into an int, and float() of it raises OverflowError. A validator must not crash on any input a model can write.

A validator should not answer only yes or no. It should return a list of everything that is wrong, each error tied to a path, the address of the bad value inside the arguments. That list is what goes back to the model when we ask it to repair the call. A model told $.items[2].qty: expected integer can fix the one thing that is wrong. A model told "invalid" can only guess. In an interview, "what does your validator return?" is the question that separates a toy from a production design.

Warm-up

Validate a tool call

Write validate(instance, schema) for the core of JSON Schema defined above, the keywords type, properties, required, additionalProperties (false or a schema), items, enum, minimum, maximum, and anyOf. Return a list of every error, and never stop at the first one. Each error starts with a path. A path begins at $, adds .key for an object property, and adds [i] for an array element, so the quantity of the third item is $.items[2].qty. Use exactly these messages, where path is the path of the value being checked.

  • f"{path}: expected {t}" when type fails, with a list of types joined by " or ". When type fails, report nothing else for that value.
  • f"{path}: missing required property '{k}'", where path is the path of the object.
  • f"{path}: unexpected property '{k}'" for a key that additionalProperties: false rejects, again with the object's path.
  • f"{path}: not in enum", f"{path}: below minimum {m}", f"{path}: above maximum {m}", and f"{path}: matches no schema in anyOf".

Compare enum values with JSON equality at every depth. So [true] is not in the enum [[1]], but [1.0] is.

Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.

One call, end to end

A tool call passes through six steps between the schema and the function, and every production bug lives in one of them. One term first. A chat template is the fixed recipe that turns a conversation (system message, user turns, assistant turns, tool results) into one string of tokens for the model. Special tokens mark where each message starts and ends (§14.3).

  1. Render. The serving stack writes the tool definitions into the prompt, usually inside the system message, using the chat template. Anthropic's API does the same with its own wording. Its documentation says the API "constructs a special system prompt from the tool definitions", which is why tool definitions cost input tokens on every request.
  2. Generate. The model samples tokens as usual (§6.4). It may write some text first, then one call, several calls, or none at all.
  3. Stop. The turn ends at the end-of-turn token. In many open models the markers that open and close a call are single tokens added to the vocabulary, so opening a call is one decision, learned like any other token (§2.5).
  4. Parse. The server finds the calls in the text and decodes their JSON. This is the parser of the agent loop, and it can fail. The JSON may be malformed, the name may be missing or unknown, or the token limit may have cut the output off in the middle of a call.
  5. Validate. The arguments are checked against the tool's schema, with the validator you just wrote.
  6. Execute and return. The function runs. Its result, or its error, goes back into the context as a tool message tied to the call by an id, a short unique string attached to the call. The model reads the result as its next observation, and the loop continues.

Hosted APIs hide steps 1 and 4 behind structured objects, but the six steps are the same everywhere. The table maps them onto the two largest APIs and the Qwen2.5 template.

Anthropic Messages APIOpenAI Chat Completionsopen weights (Qwen2.5 template)
tool definitionstools[] with input_schematools[] with function.parametersrendered into the system turn
the calla tool_use block with id, name, and input (an object)tool_calls[] with id, function.name, and function.arguments (a JSON string)JSON between <tool_call> tokens
the resulta tool_result block with tool_use_id, and is_error for failures, in a user messagea message with role tool and tool_call_id<tool_response> in a user turn
choosingtool_choice of auto, any, tool (a named one), or nonetool_choice of auto, required, none, or a named functionthe prompt only, unless you constrain decoding
several calls at onceon by default, and disable_parallel_tool_use turns it offparallel_tool_callsseveral blocks in one turn

Two rows of the table hide design decisions. The first is forcing a call. The tool_choice setting can make the model call some tool (any or required) or one named tool. Anthropic implemented this with a prefill. Its documentation says that with any or a named tool "the API prefills the assistant message to force a tool to be used". To prefill is to write the start of the model's reply for it, so the turn begins inside the call. The model then writes no explanation and no reasoning before the call. The newest Claude models (Opus 5.5, Sonnet 5.5, and Fable 5.1, as of October 2026) reject forced calls with an error. The documentation recommends auto, a strict schema, and an instruction in the prompt instead.

The second is parallel calls, several calls in one turn. Their results may come back in any order, and the id is the only thing that binds a result to its call. Templates without ids, like Qwen2.5's, match results to calls by position, which breaks as soon as a result is dropped or reordered.

Here is the whole loop against a real SDK. It cannot run in your browser, which has no network, but every line is one of the six steps. The runnable cells in this section replace the model with a short script that has the same interface, messages in and text out. The loops are real, and only the decisions are hand-written.

The strict flag asks the provider to constrain decoding to the schema, which is the subject of most of this section. Notice the range check inside the function body. Anthropic's strict mode, as documented in October 2026, does not support minimum and maximum, and a raw request that uses them is rejected. Its SDK helpers remove such keywords before sending and copy them into the field's description, and the helpers that parse the response check them on the client afterward. So some rules must still be checked after the fact, by your own code.

With open-weight models you write step 4 yourself, and it is a classic interview problem. The model's output is raw text. You must find every call in it, survive every way it can be broken, and never pass a bad call through.

Interview

Parse tool calls from raw text

Write parse_tool_calls(text, tool_names) for the Qwen2.5 format. Each call is a JSON object {"name": ..., "arguments": ...} between <tool_call> and </tool_call>. Several calls may appear, with any text (newlines included) around and between them. Return one result per block, in the order the blocks appear.

  • A good call gives {"name": name, "arguments": args} with args a dict. A missing "arguments" key means {}. If "arguments" is a string (the OpenAI convention), decode it as JSON once, and the result must be a dict.
  • Content that is not valid JSON, or that json.loads cannot decode for any other reason, gives {"error": "malformed_json", "raw": content}, with content the stripped text between the tags.
  • A JSON value that is not an object, or that has no string "name", gives {"error": "missing_name", "raw": content}.
  • A name not in tool_names gives {"error": "unknown_tool", "name": name}.
  • Arguments that are not an object after decoding a string, null and [] included, give {"error": "bad_arguments", "name": name}.
  • If an opening <tool_call> remains after the last complete block, because the output was cut off, append {"error": "unterminated", "raw": rest} with rest the stripped text after that tag.

Never raise an exception, whatever the input.

Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.

Validation, repair, and the arithmetic of retries

The second line of defense treats a validation error as an ordinary observation. The server does not crash or give up. It sends the list of errors back to the model as the tool's result, and the model tries again. Protocols now say so explicitly. The Model Context Protocol is the open protocol for connecting models to tool servers, and §24.6 takes it apart. Its 2025-11-25 revision makes invalid arguments a tool execution error, returned to the model so it can correct itself, rather than a protocol error that ends the request.

Here is a repair loop. The policy is a scripted stand-in for a model. Its first call has two mistakes, since days is the string "3" and there is an extra units argument. On each retry it fixes whatever the error list names.

Python · cell 2

How much does repair buy? §24.1 split error handling into two probabilities. The chance that an error is detected is dd, and the chance that a detected error is repaired is qq. A validator catches every schema violation, so for this kind of error d=1d = 1, and only the repair rate matters. The new question is what a budget of several retries buys.

In plain words, each allowed retry multiplies the leftover failure rate by 1−q1-q, while the cost in turns levels off at Ncε/qN_c\varepsilon/q. The first retry buys the most. Later retries are cheap, because they are rarely needed.

∂ Proof of the retry formulas

A call stays invalid if and only if its first attempt is malformed and all kk repairs fail. By independence that has probability ε(1−q)k\varepsilon(1-q)^k. The NcN_c calls are independent, so the probability that none stays invalid is (1−ε(1−q)k)Nc(1 - \varepsilon(1-q)^k)^{N_c}, and one minus that is the first formula.

For the turns, take one malformed call. It uses at least jj repairs exactly when the first j−1j - 1 repairs all failed, which has probability (1−q)j−1(1-q)^{j-1}, for j=1,…,kj = 1, \dots, k. The expected value of a count that takes values 0,…,k0, \dots, k is the sum, over jj from 1 to kk, of the probability that it is at least jj. So

E[repairs]=∑j=1k(1−q)j−1=1−(1−q)kq,\mathbb{E}[\text{repairs}] = \sum_{j=1}^{k}(1-q)^{j-1} = \frac{1 - (1-q)^k}{q},

a geometric sum. The expected number of malformed calls is NcεN_c\varepsilon, and multiplying gives the second formula.

Python · cell 3

Two cautions keep this honest. First, repairs are not independent in practice. A model that misread the schema once tends to misread it again, so the real failure rate falls more slowly than (1−q)k(1-q)^k. Second, a validator can only detect what the schema can express. A call to place_order with the right types and the wrong customer is valid, and for that kind of error dd is far below 1. Repair is worth exactly as much as the error messages are specific, which is why the exercise insisted on paths. An interviewer who asks "how many retries would you allow?" wants this formula, and then these two caveats.

Making malformed output impossible

Repair fixes mistakes after they are made, at the price of a turn. The third defense prevents them. Here is the idea in plain words. At every step the model gives a probability to every token in its vocabulary. Before the next token is chosen, we cross out every token that would make a valid output impossible, and we choose only among the tokens that remain. If we cross out exactly the tokens that lead to dead ends, the output is valid by construction.

Crossing out is easy. Each token has a logit, the score the model computes for it before the softmax turns scores into probabilities. Setting a token's logit to −∞-\infty makes its probability exactly zero. This is the same masking trick that §6.4 uses for top-k sampling. The hard part is deciding, fast, which tokens to cross out. For that we need a description of the valid strings that a program can step through one character at a time. The right description is a finite automaton, a small machine with a few states and edges labeled by characters. Here is one, small enough to run by hand.

Now the general picture. Let Σ\Sigma be the set of characters (in practice bytes, as in §2.3). Let V\mathcal{V} be the vocabulary, each token a nonempty string over Σ\Sigma, and let EOS be the end token.

In plain words, δ\delta says where one character takes you, δ∗\delta^* says where a whole string takes you, and the language is the set of strings that end in an accepting state. A live state is one from which acceptance is still reachable. In the yes-or-no example every state is live.

Two notes on symbols. The subscript in LG\mathcal{L}_G (for "grammar") keeps the language apart from a loss L\mathcal{L}. And from here on a plain qq always denotes a state. The repair probability qq of the previous part does not appear again, and qlocq_{\text{loc}}, defined below, is a distribution, not a state.

One identity does the work in everything that follows. Reading a string stst is the same as reading ss and then tt, so δ∗(q,st)=δ∗(δ∗(q,s),t)\delta^*(q, st) = \delta^*(\delta^*(q, s), t), by induction on the length of tt. That is why we may feed the automaton one token at a time, even though tokens are several characters long.

In plain words, the index answers "which tokens keep the output on track from here?" for every state, in advance. Decoding zeroes out every other token and rescales the rest to sum to one. ZtZ_t is the probability the model put on allowed tokens, so 1−Zt1 - Z_t is the probability the mask removed. Engines implement the mask by adding −∞-\infty to every disallowed logit before the softmax. We write pθp_\theta for the model so that pp alone keeps its §24.1 meaning of a per-step success rate.

The key design choice is to compute the index once, before any decoding, for each pair of a pattern and a vocabulary. Each decoding step then costs a table lookup instead of a scan of the whole vocabulary. This idea, compiling a regular expression to an automaton and precomputing its allowed tokens per state, is the core of the Outlines library (Willard and Louf 2023). The widget runs it on three patterns, a date, a JSON object whose one field must be one of three words, and a signed integer.

Which tokens survive the mask at each step, and how much probability does it remove?
A toy model that likes to chat decodes under three formats. Masked tokens are struck out.
Loading interactive figure…
Core

The vocabulary index

A DFA is given as a dict with three keys. "start" is the start state, and "accept" is the set of accepting states. "delta" maps every state to a dict from characters to next states. Every state appears as a key of "delta", possibly with no edges. The vocabulary is a list of nonempty strings, and EOS has id len(vocab). Write two functions.

  • live_states(dfa) returns the set of live states.
  • build_index(dfa, vocab) returns a dict that maps every state to its set of allowed token ids, exactly as in the definition of allow⁡\operatorname{allow}.
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.

The yes-or-no example showed that a correct index can still strand the decoder. The fix is one extra assumption, that every single character is also a token. With it we can prove that constrained decoding does what it promises.

In plain words, if single characters are always there as a fallback, the decoder can always take one more step toward a valid string, and it can only stop when the string is valid.

∂ Proof of soundness and progress

We show by induction on tt that the state qt=δ∗(q0,y1⋯yt−1)q_t = \delta^*(q_0, y_1\cdots y_{t-1}) is defined and live. At t=1t = 1, q1=q0q_1 = q_0, which is live because LG\mathcal{L}_G is nonempty. Suppose qtq_t is live. Any allowed token v≠EOSv \ne \text{EOS} has δ∗(qt,v)\delta^*(q_t, v) defined and live by the definition of the index, and δ∗(q0,y1⋯yt−1v)=δ∗(qt,v)\delta^*(q_0, y_1\cdots y_{t-1}v) = \delta^*(q_t, v) by composition, so qt+1q_{t+1} is live.

Now (1). If qt∈Fq_t \in F, EOS is allowed. Otherwise, since qtq_t is live, some nonempty s=c s′s = c\,s' has δ∗(qt,s)∈F\delta^*(q_t, s) \in F. So δ(qt,c)\delta(q_t, c) is defined and live (it reaches FF by s′s'), and the token cc is in V\mathcal{V} by hypothesis, hence in allow⁡(qt)\operatorname{allow}(q_t).

For (2), EOS is chosen only in a state of FF, and that state is δ∗(q0,text)\delta^*(q_0, \text{text}), so the text is in LG\mathcal{L}_G.

For (3), take s=c1⋯cm∈LGs = c_1 \cdots c_m \in \mathcal{L}_G and tokenize it character by character. Each state δ∗(q0,c1⋯ci)\delta^*(q_0, c_1\cdots c_i) is live, because the rest of ss leads it to FF, so each ci+1c_{i+1} is allowed. EOS is allowed at the end because δ∗(q0,s)∈F\delta^*(q_0, s) \in F. Each choice has positive probability under qlocq_{\text{loc}} when pθp_\theta is positive on every token. ∎

Byte-level vocabularies satisfy the assumption automatically, since all 256 bytes are tokens (§2.3). Without it, the index must also be pruned by what tokens, not characters, can reach. Call a state token-live if some sequence of allowed tokens leads from it to EOS. The token-live states are found by a fixpoint computation, one that repeats an update until nothing changes. It starts from the states that allow EOS and keeps adding states that have an allowed token into the set. Then every token that leads to a state outside the set is removed. The first cell builds the index for the yes-or-no example, and the second prunes it.

Python · cell 4
Python · cell 5

State 2 is the one token-dead state, and pruning removes ye from the start, exactly the trap of the hand example. What remains is the price of the index.

So the index turns the mask from a bottleneck into noise. That is the answer an interviewer wants to "why precompute?", together with its price, one stored mask per automaton state.

The automaton offers a second saving, in forward passes rather than in mask work. A tool call is full of text the model has no choice about, such as the braces and key names of its arguments. Masked decoding still spends one forward pass per forced token. Jump-forward decoding, built in §20.7, appends the whole forced string in one pass instead, as a small prefill chunk, and retokenizes the seam with the text already written so the model never sees a split it was not trained on. The SGLang serving system measured 1.6 times the throughput on JSON decoding from this (Zheng et al. 2024). For an agent, whose every step emits a call with fixed key names, that saving recurs on every turn of the loop.

The next exercise is the decoding loop itself. It is a favorite in interviews, because each line has a classic bug.

Interview

Constrained decoding

Write constrained_decode(logits_fn, dfa, vocab, index, max_len, greedy=True, rng=None). Here logits_fn(tokens) takes the list of token ids generated so far and returns a NumPy array of len(vocab) + 1 finite logits, the last one for EOS. Logits can be large, so sampling must not overflow.

  • Start in dfa["start"], and track the state with step from the setup.
  • At each step, allow only the tokens in index[q].
  • With greedy=True, take the allowed token with the largest logit. Otherwise sample from the model's distribution renormalized over the allowed tokens, using rng.choice.
  • Return the list of ids, including the final EOS.
  • Return None if the allowed set is ever empty, or if max_len tokens (EOS included) pass without EOS.

Never return an output outside the language.

Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.

Why JSON needs a stack

A language that some finite automaton recognizes is called regular. Dates, fixed sets of words, and integers are regular. JSON is not, because objects and arrays nest to any depth, and a machine with finitely many states cannot keep count of how many brackets are open. Here is the precise statement.

∂ Why a finite automaton cannot match brackets

Suppose a DFA with NQ=∣Q∣N_Q = |Q| states recognizes {[k]k}\{[^k]^k\}. Read the string [NQ]NQ[^{N_Q}]^{N_Q}, which it must accept. While reading the NQN_Q opening brackets it passes through NQ+1N_Q + 1 states δ∗(q0,[0),…,δ∗(q0,[NQ)\delta^*(q_0, [^0), \dots, \delta^*(q_0, [^{N_Q}). By the pigeonhole principle two of them coincide, say δ∗(q0,[i)=δ∗(q0,[j)\delta^*(q_0, [^i) = \delta^*(q_0, [^j) with i<ji < j. The automaton then reaches the same state after [i[^i as after [j[^j. So, by the composition of δ∗\delta^*, it reaches the same final state on [NQ−(j−i)]NQ[^{N_Q - (j - i)}]^{N_Q} as on [NQ]NQ[^{N_Q}]^{N_Q}, and accepts it. But that string has fewer opening than closing brackets. This contradiction shows that no such DFA exists.

For the bound, let the two kinds be [ ] and { }, and consider the ∑k=0D2k=2D+1−1\sum_{k=0}^{D} 2^k = 2^{D+1} - 1 strings of at most DD opening brackets. Two different ones, w≠w′w \ne w', must lead to different states. Let c(w)c(w) be the closing string that matches ww, its closers in reverse order. Then w c(w)w\,c(w) is balanced and accepted, while w′ c(w)w'\,c(w) is not, because either the lengths differ or some closer meets an opener of the other kind. If ww and w′w' led to the same state, the automaton would treat the suffix c(w)c(w) the same way after both. So there are at least 2D+1−12^{D+1} - 1 distinct states.

In plain words, an automaton's only memory is its current state, and remembering which brackets are open takes a stack. A depth limit makes JSON regular again. But then the automaton must spell out every possible stack of open brackets as its own state, and their number grows exponentially with the depth. Real schemas also nest strings, numbers, and keys inside each level, which multiplies the count. In an interview, "why not just a regex for JSON?" is answered by this pigeonhole argument.

The fix is to give the machine a stack, a list it can add to at the top (push) and remove from at the top (pop). A pushdown automaton is a finite automaton that also keeps a stack. At each character it looks at its state and the top symbol of the stack, moves to a new state, and may push or pop. Here is one that checks a small piece of JSON.

For reference, here are the two formal objects behind this example, a grammar that generates the valid strings and a machine that recognizes them.

In plain words, a grammar says how to build valid strings piece by piece. A pushdown automaton checks a string from left to right while keeping on its stack the pieces it has opened and not yet closed. This machine moves only when it reads a character, one move per character, which is what a decoder wants.

∂ The grammar for nested arrays, and which machines recognize which languages

The nested arrays of the example have a grammar with the nonterminals A\mathsf{A} (an array, the start symbol), E\mathsf{E} (an element), M\mathsf{M} (more elements), N\mathsf{N} (an integer), and D\mathsf{D} (more digits). The productions are

A→[ ]  ∣  [ E M ],M→⟨⟩  ∣  , E M,E→N  ∣  A,N→0  ∣  p D,D→⟨⟩  ∣  d D,\begin{aligned} \mathsf{A} &\to [\,] \;\mid\; [\,\mathsf{E}\,\mathsf{M}\,], & \mathsf{M} &\to \langle\rangle \;\mid\; ,\,\mathsf{E}\,\mathsf{M}, & \mathsf{E} &\to \mathsf{N} \;\mid\; \mathsf{A}, \\ \mathsf{N} &\to 0 \;\mid\; p\,\mathsf{D}, & \mathsf{D} &\to \langle\rangle \;\mid\; d\,\mathsf{D}, & & \end{aligned}

where ∣\mid separates alternatives, pp stands for any digit 1 to 9, and dd for any digit, so integers have no leading zeros, as in JSON. The production E→A\mathsf{E} \to \mathsf{A} is the recursion that no DFA can follow. A leftmost derivation of [1,[2]], which rewrites the leftmost nonterminal at every step, is

A⇒[EM]⇒[NM]⇒[1DM]⇒[1M]⇒[1,EM]⇒[1,AM]⇒[1,[EM]M]⇒[1,[NM]M]⇒[1,[2DM]M]⇒[1,[2M]M]⇒[1,[2]M]⇒[1,[2]].\begin{aligned} \mathsf{A} &\Rightarrow [\mathsf{E}\mathsf{M}] \Rightarrow [\mathsf{N}\mathsf{M}] \Rightarrow [1\mathsf{D}\mathsf{M}] \Rightarrow [1\mathsf{M}] \Rightarrow [1,\mathsf{E}\mathsf{M}] \Rightarrow [1,\mathsf{A}\mathsf{M}] \Rightarrow [1,[\mathsf{E}\mathsf{M}]\mathsf{M}] \\ &\Rightarrow [1,[\mathsf{N}\mathsf{M}]\mathsf{M}] \Rightarrow [1,[2\mathsf{D}\mathsf{M}]\mathsf{M}] \Rightarrow [1,[2\mathsf{M}]\mathsf{M}] \Rightarrow [1,[2]\mathsf{M}] \Rightarrow [1,[2]]. \end{aligned}

At every moment of the machine's run, its stack holds one [ for each ] that this derivation still owes.

General pushdown automata may also move without reading a character, and may be nondeterministic. The nondeterministic ones recognize exactly the languages of context-free grammars, called the context-free languages. The deterministic ones recognize a strict subclass. Sipser's textbook Introduction to the Theory of Computation proves both facts in its chapter on context-free languages. JSON lies in the subclass, and the one-move-per-character machine of the example is enough for it.

The same construction scales to real formats. The subset of JSON Schema that tool definitions use (type, properties, required, enum, items, anyOf, and $ref, a reference to another schema) compiles to a context-free grammar. There is one rule per subschema, and recursion wherever a $ref points back to an enclosing schema. Engines usually also fix the order of an object's keys to the order in the schema, as OpenAI's Structured Outputs does. The reason is that a grammar allowing every order of nn required keys grows exponentially in nn.

Some keywords do not fit. uniqueItems, not, and rules that tie fields together (dependentRequired, and if with then and else) are not context-free in general, or they blow up the grammar. So do large minItems, maxItems, and maxLength bounds, which must be unrolled into counted copies of a rule. Engines reject such schemas, drop the keywords silently, or check them after decoding. A benchmark later in the section measures how large this gap is.

Constrained decoding with a grammar keeps a configuration (a state and a stack) in place of a single DFA state, and the theory so far carries over. The proof of soundness and progress used three facts. Allowed tokens lead to live states, every single character is a token, and δ∗\delta^* composes over concatenation. None of them needs finitely many states, so the theorem holds word for word with configurations in place of states.

What fails is the index. A pushdown automaton has infinitely many configurations, one for every stack, so allow⁡\operatorname{allow} cannot be tabulated in advance.

Liveness, at least, needs no search here. For the machine above, every configuration that a valid prefix reaches is live, because closing every open array finishes the output. So a token is allowed exactly when the walk through its characters survives. Exact prefix parsers for any grammar behave the same way, since they fail as soon as the text stops being the start of some valid string. The cell implements the machine and walks tokens through it. These include ]] and ],, which pop and keep reading, and [[, which pushes twice.

Python · cell 6
Python · cell 7

Look at ]] and ], after [1 and after [[1. Both times the state is num and the top of the stack is [. Yet the tokens are rejected the first time and allowed the second. Each token pops the open array and then reads one more character. That character is checked against the symbol beneath the old top, and only the full stack says whether that symbol is another [ or the bottom marker.

This splits the vocabulary in two. Call a token context-dependent at a configuration if reading it pops the symbol that was on top when it began and then reads another character. Call it context-independent otherwise. The verdict on a context-independent token depends only on the state and the top symbol. So it can be precomputed once per pair of a state and a top symbol, exactly like the DFA index. Only the context-dependent tokens need the full stack while decoding. The widget lets you build a nested array token by token and see how few they are.

Which tokens can only be judged by looking down the stack?
Click tokens to build a nested array. Dashed tokens close the open array and keep reading.
Loading interactive figure…
Interview

Constrained decoding for nested arrays with a stack

Constrain a model to write one JSON array of nonnegative integers, nested to depth at most max_depth, without spaces. This is the language of the example above with a depth limit, and max_depth is at least 1. So [], [0,[12,[]]], and [[7]] are valid, while [1,], [,1], [07], [1]], 1, and the empty output are not. Write four functions.

  • init_config() returns the configuration before anything is read, in any representation you like.
  • advance(config, s, max_depth) reads the characters of the string s and returns the new configuration. It returns None as soon as the text can no longer be the start of a valid output, which includes opening an array at depth max_depth + 1. advance(config, "", max_depth) returns config.
  • allowed_tokens(config, vocab, max_depth) returns the set of token ids allowed next. Here vocab is a list of nonempty strings and EOS has id len(vocab). EOS is allowed only after a complete top-level array.
  • nested_decode(logits_fn, vocab, max_depth, max_len) decodes greedily as in Exercise 24.2c, with logits_fn(tokens) returning len(vocab) + 1 logits for the list of ids so far. Return the ids including the final EOS, or None if max_len tokens pass without EOS. Never return an output outside the language.

Every configuration that a valid prefix reaches can still be completed. The vocabulary always contains [, ], ,, and the digits as single tokens. So a token is allowed exactly when advance survives it.

Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.

The same split, at the scale of a real grammar and a real vocabulary, is how the fastest engines work. The XGrammar engine (Dong et al. 2024) compiles the grammar to a pushdown automaton over bytes, whose states are positions inside grammar rules. It sorts tokens into two kinds.

  • A token that enters a child rule, or moves forward within the current rule, is context-independent. Its verdict is precomputed and stored in a mask cache, keyed by the position on top of the stack.
  • A token that reaches the end of the current rule and must continue in the parent rule is context-dependent. Only these are checked against the full stack while decoding.

OpenAI's announcement of Structured Outputs (August 6, 2024) gave the reason for grammars directly. Its example is a recursive schema for a user interface, whose components contain components, which a finite-state approach cannot express. Not every provider goes that far. Anthropic's strict mode, as of October 2026, rejects recursive schemas.

Constrained decoding changes what the model says

Constrained decoding guarantees that the output parses. It does not guarantee that the output follows the distribution you would get by sampling the model freely and throwing away every invalid output. Those are two different distributions, and the gap between them can be large. §20.7 met this gap from the serving side, in a hand example where the mask made the model's least favorite valid string its most likely one. Here we measure it, prove when it appears, and correct it, starting from the smallest example where the two distributions come apart.

The general statement needs one quantity, the probability that the model, continuing from a prefix, ends up with a valid output.

In the example, acc⁡(a)=0.01\operatorname{acc}(a) = 0.01, acc⁡(b)=1\operatorname{acc}(b) = 1, and acc⁡(⟨⟩)=0.109\operatorname{acc}(\langle\rangle) = 0.109. Park et al. (2024) call this quantity the expected future grammaticality. By the law of total probability over the next token (§0.4), it satisfies a one-step recursion, acc⁡(y<t)=∑vpθ(v∣y<t) acc⁡(y<tv)\operatorname{acc}(y_{<t}) = \sum_{v} p_\theta(v \mid y_{<t})\,\operatorname{acc}(y_{<t}v).

In plain words, exact conditioning weights each next token by how likely the model is to finish validly after it. Local masking only asks whether finishing validly is possible, a yes-or-no stand-in for a probability. When some allowed tokens lead into paths the model itself is unlikely to complete, local masking gives them too much weight. In the example, a had acceptance probability 0.01 and b had 1, and local masking treated them as equals.

∂ Proof of the distortion theorem

The product of the ratios telescopes, ∏t=1Tacc⁡(y≤t)/acc⁡(y<t)=acc⁡(y)/acc⁡(⟨⟩)=1/pθ(L)\prod_{t=1}^{T}\operatorname{acc}(y_{\le t})/\operatorname{acc}(y_{<t}) = \operatorname{acc}(y)/\operatorname{acc}(\langle\rangle) = 1/p_\theta(L). So the right side is pθ(y)/pθ(L)p_\theta(y)/p_\theta(L), which is the conditional. Each factor sums to one over yty_t by the one-step recursion.

For the comparison, fix a reachable prefix. If acc⁡(y<tv)=c\operatorname{acc}(y_{<t}v) = c for every allowed vv, then acc⁡(y<t)=cZt\operatorname{acc}(y_{<t}) = cZ_t, because a disallowed token falls off the automaton or leads to a dead state, so its acceptance probability is 0. Then the exact factor is pθ(v∣y<t) c/(cZt)p_\theta(v \mid y_{<t})\,c/(cZ_t), the local one. Conversely, suppose the two procedures give every complete sequence the same probability. Then they give every prefix the same probability too, since that is a sum over the complete sequences that extend it. Their next-token factors are ratios of prefix probabilities, so they agree at every reachable prefix. Equating them for an allowed vv gives acc⁡(y<tv)=acc⁡(y<t)/Zt\operatorname{acc}(y_{<t}v) = \operatorname{acc}(y_{<t})/Z_t, the same value for every allowed token.

Finally, qloc(y)=∏tpθ(yt∣y<t)/Ztq_{\text{loc}}(y) = \prod_t p_\theta(y_t \mid y_{<t})/Z_t because every token of yy is allowed, which gives the ratio. ∎

Two consequences matter in practice. The first says when the distortion is small. Suppose acc⁡(y<tv)≥1−η\operatorname{acc}(y_{<t}v) \ge 1 - \eta for every allowed token vv at every reachable prefix, for some small η\eta. Then acc⁡(y<t)\operatorname{acc}(y_{<t}) lies between (1−η)Zt(1-\eta)Z_t and ZtZ_t, so each local factor is within a factor 1/(1−η)1/(1-\eta) of the exact one in either direction. The distortion is small exactly when the model already writes valid output almost always.

So constrained decoding is safest on models trained to follow schemas, and distorts most on the models that need it most. OpenAI did both, training gpt-4o-2024-08-06 on schemas and then constraining it, which is the combination this result favors.

The second consequence is an exact correction. Weight each locally masked sample by ∏tZt\prod_t Z_t, a number the decoder computes anyway, and resample (or average) with those weights. The result targets the true conditional. This is importance sampling, drawing from an easy distribution and reweighting the samples to stand for a hard one, here with local masking as the easy distribution. An interviewer who asks "does constrained decoding change the model's answers?" wants the elevenfold example, the acceptance ratio, and this reweighting.

The widget is the hand example with its probabilities on sliders. The third slider is new. It sets the chance that the model stops after b instead of writing something outside the language. Move the sliders and watch local masking and the exact conditional separate or agree. They agree exactly when pθ(b∣a)=pθ(EOS∣b)p_\theta(b \mid a) = p_\theta(\text{EOS} \mid b), because those are the acceptance probabilities of a and b, and the theorem asks them to be equal.

Does masking sample from the model conditioned on the grammar?
The valid outputs are ab and b. Masked decoding against the model conditioned on writing one of them.
Loading interactive figure…

Let us measure the distortion at a scale where the hand calculation is out of reach. The model is a bigram language model, one whose next-token distribution depends only on the previous token, with random parameters. Its vocabulary has 30 tokens, among them digits, punctuation, two-digit tokens like 12, hyphen tokens like -3, two word pieces, and EOS. The format is a date-like DD-DD. The first cell sets this up, and the second decodes greedily with and without the mask.

Python · cell 8
Python · cell 9

Unconstrained, the model rambles through digits and does not stop within eight tokens. Constrained, it writes 30-32 in three tokens and EOS. That string parses perfectly, and it is not a date anyone has seen, since there is no month 30 and no day 32. At two of the steps the mask removed 86% and then 91% of the model's probability, a sign that the model and the format disagree badly there.

Now the exact conditional. For a bigram model, the acceptance probability depends only on the previous token, the automaton state, and the number of tokens left. So it obeys a recursion we can memoize, caching each value the first time it is computed. With it we can list every output that local masking can produce and compare the two distributions exactly.

Python · cell 10

With a neural model, the recursion for acc⁡\operatorname{acc} cannot be computed, because it sums over all continuations of every prefix. That is why exact sampling from the conditional remains a research problem, and why production engines mask locally.

Research

Grammar-aligned sampling

A bigram language model is given by two arrays. P0, of shape (V + 1,), is the first-token distribution. P, of shape (V, V + 1), has P[u, v] equal to the probability of v after u. Token ids are 0 to V - 1 for vocab and V for EOS. A valid output has text in the DFA's language and ends with EOS within max_len tokens, EOS included.

  1. Write exact_next(P0, P, dfa, vocab, prefix, max_len). It returns the exact conditional next-token distribution pθ(v∣y<t)acc⁡(y<tv)/acc⁡(y<t)p_\theta(v \mid y_{<t})\operatorname{acc}(y_{<t}v)/\operatorname{acc}(y_{<t}), an array of shape (V + 1,). The prefix is a valid list of ids with positive acceptance probability, and the acceptance probability counts only completions that finish within the remaining budget of max_len - len(prefix) tokens.
  2. Write kl_local_vs_exact(P0, P, dfa, vocab, index, max_len). It returns DKL(qloc ∥ pθ(⋅∣L))D_{\mathrm{KL}}(q_{\text{loc}} \,\Vert\, p_\theta(\cdot \mid L)) in nats, where qlocq_{\text{loc}} masks with index. You may assume that local masking always finishes within max_len.
Tests, hints, verified solutions, and AI help, with Pro at $7.99 a month.

In PyTorch

In a real engine the index is a boolean tensor of shape (states, V), and constraining a step is one masked_fill on the logits. Here a character-level bigram model is trained on a few sentences that mention dates. It is then decoded greedily, with and without a mask for the pattern DDDD-DD-DD.

🔥 In PyTorchfor a machine with PyTorch

Unconstrained greedy decoding falls into the repetition loop 2020... that §6.4 warned about. The mask forces the hyphens in, and the model fills in plausible digits around them. Production engines (XGrammar, Outlines, and the grammar backends of the vLLM and SGLang servers) do exactly this. They build the mask from a compiled grammar and apply it to a batch of logits on the GPU.

Token boundaries, and what format does to reasoning

The index above judges whole tokens against the automaton. So it allows a token that spans two parts of the grammar, such as a space followed by a quotation mark. Many systems do not work that way. A template engine writes the fixed parts of the output itself (the braces, the key names) and lets the model fill in only the values. It tokenizes the fixed text separately, and that produces token sequences the model never saw in training.

Beurer-Kellner et al. (2024) measured this. They used perplexity, the exponential of the average negative log-probability per token, which is low when the model finds a text natural. Constraining Mistral-7B with a fixed template raised the perplexity of its output from 4.17 unconstrained to between 24.50 and 26.75. On a JSON version of the GSM8K math benchmark, such misaligned constraining cut accuracy from 41.5% to 30.8%, while their aligned method reached 41.8%. Two fixes are in use.

  • Token healing (§2.6) backs the prompt up by its last token and constrains the first generated token to start with the removed text. The model can then choose the merged token itself.
  • Subword-aligned decoding precomputes how the grammar's pieces and the vocabulary's tokens overlap, so that tokens bridging two pieces of the grammar stay available. The authors' algorithm, DOMINO, does this at little or no cost in speed.

A related question is whether forcing a format hurts reasoning. The studies disagree at first sight, and the deeper box sorts them out. The reading that fits all of them is that format constraints are not free, but their cost depends mostly on how you prompt and on where the format puts the reasoning.

Training models to call tools

Constrained decoding guarantees syntax. Choosing the right tool with the right arguments, and knowing when not to call a tool at all, has to be learned. The data recipes have converged on four steps. Collect real APIs, write user requests for them, generate candidate calls and check them, and fine-tune on the calls that pass.

Three projects show the recipe growing up.

  • Training with the documentation in the prompt. Gorilla (Patil et al. 2023) fine-tuned LLaMA on 1,645 machine-learning APIs from Hugging Face, TorchHub, and TensorFlow Hub, with ten generated instructions for each. During training the relevant documentation was retrieved and placed in the prompt, so the model learned to follow documentation that may change at test time. It scored calls by comparing their abstract syntax trees, the parsed structure of a call with its function name and arguments, against the reference. That comparison also catches calls to APIs that do not exist.
  • Real APIs at scale, with search. ToolLLM (Qin et al. 2023) collected 16,464 real web APIs in 49 categories from the RapidAPI marketplace and had ChatGPT write single-tool and multi-tool instructions. It found solution paths by a depth-first search over trees of calls, which can back up from a failed call, instead of following a single greedy chain.
  • Checking at the center. APIGen (Liu et al. 2024) generated candidate calls for 3,673 executable APIs in 21 categories. Each candidate must pass three checks in order, its format, an actual execution, and a semantic check that the result answers the query. A 6.7B model trained on the checked data ranked sixth on the Berkeley Function Calling Leaderboard at the time, above GPT-4o and Gemini-1.5-Pro. The authors released 60,000 of the checked examples.

Two details separate good data from bad. The first is negative examples. A model trained only on conversations that end in a call learns to call something every time. So the data must include requests that no available tool can serve, where the right output is a plain answer, and the evaluation must measure this (the relevance tests below). The second is the loss mask of §14.3, the per-token weight that decides which tokens are trained. Train the tokens the model writes, including the call and its end-of-turn token. Mask the tool definitions, the user turns, and above all the tool results, which come from the environment. A model trained on tool results learns to invent them.

Reinforcement learning sharpens what supervised data starts. The key design choice is the reward, and the lesson so far is to give graded credit for partly right calls rather than all or nothing. ToolRL (Qian et al. 2025) trained Qwen2.5 and Llama 3.2 models this way with GRPO, the policy-gradient method that scores each sample against the others drawn for the same prompt (§18.4). Its models gained about 17% over their base models and 15% over supervised fine-tuning. Multi-turn agent RL, where whole trajectories of calls and observations are rewarded and the observation tokens are masked from the gradient, is the subject of §24.3.

Evaluating function calling

A function-calling benchmark has to decide what "correct" means for a call, and there are two answers.

  • An AST check parses the call and compares its abstract syntax tree with a set of acceptable answers, covering the function name, every required argument, and each value's type and content. It is cheap, deterministic, and needs no running services.
  • An execution check runs the call and compares the result. It is expensive and can be flaky, but it catches calls that are well formed and wrong in ways a reference answer did not anticipate.

The Berkeley Function Calling Leaderboard (BFCL, from the Gorilla team) uses both. Its first release sorted single-turn calls into these categories.

  • Simple, one function and one call.
  • Multiple, choosing one of 2 to 4 functions.
  • Parallel, several calls to one function from one request.
  • Parallel multiple, several calls to several functions.
  • Relevance detection, where none of the functions fit and the right output is no call at all.

Its AST rules are strict on types, and one of them contradicts JSON Schema. For Python functions it accepts an integer where a float is expected, but never a float where an integer is expected. So 3.0 for an integer parameter is wrong in BFCL and valid in JSON Schema. Later versions added live functions contributed by users (V2), multi-turn and multi-step interactions (V3), and agentic tests of web search, memory, and sensitivity to format (V4, last updated April 12, 2026). The overall score is the unweighted average of the subcategories.

Structured output engines have their own benchmark. JSONSchemaBench (Geng et al. 2025) collects nearly 10,000 real JSON Schemas and measures six engines (Guidance, Outlines, llama.cpp, XGrammar, OpenAI, and Gemini) on efficiency, coverage, and output quality. Coverage is the fraction of schemas for which an engine produces valid output, and it is the weak point.

Here are the options side by side. JSON mode is the weakest constraint, valid JSON of any shape.

approachalways parsesarguments fit the schemachanges the model's distributionadded latencywhat it needs
prompt and parsenononononea parser and a retry path
prompt, validate, repairno, but failures are caughtafter repairs, with probability set by qq and kknoone turn per repaira validator with specific error messages
JSON modeyes, unless cut offnoyessmalla JSON grammar
schema-constrained decoding (strict)yes, unless cut off or refusedyes, for supported keywordsyes, by the distortion theoremone compile per schema, then microseconds per tokenan engine that supports your schema
fine-tuning on callsnono, but far more oftenthe model itself changesnonechecked data, with negative examples

In practice the layers stack. Fine-tune (or choose a model trained for calls), constrain decoding where the engine supports the schema, and validate anyway. The schema subset that decoding enforces is smaller than the set of rules your tool needs, so the final check belongs to your own code.

Read more

Watch next

🎬 Structured Output from LLMs: Grammars, Regex, and State Machines · Efficient NLPA clear walk from regex and finite-state masking (Outlines) to context-free grammars, pushdown automata, the token-terminal mismatch, and format restrictions versus reasoning, the arc of this section.
🎬 CMU LLM Inference (10): Incorporating Tools · Graham NeubigCMU lecture on tool use from Toolformer and Gorilla to JSON Schema function calling, parallel calls, MCP, sandboxing, and how tool use is evaluated.
🎬 Teaching LLMs to Use Tools at Scale - Shishir Patil | Stanford MLSys #98 · Stanford MLSys SeminarsThe Gorilla and BFCL author on retrieval-aware training, measuring hallucinated API calls with AST matching, and executing model-written calls safely.