6.2.10 · D3AI Agents & Tool Use

Worked examples — Guardrails and constrained generation

2,292 words10 min readBack to topic

This page is the "no surprises" drill for Guardrails and constrained generation. We will build one scenario matrix of every case class the topic can throw at you, then work each cell to the ground. If you have not yet seen how a mask turns logits into probabilities, read the parent note first — but every symbol is re-earned below from zero.

Look at the first figure: three logits go in on the left, softmax squeezes them into a bar chart of probabilities on the right that sums to .

Figure — Guardrails and constrained generation

The scenario matrix

Every question this topic can ask lands in one of these cells. The worked examples below are tagged with the cell they cover.

Cell Case class What makes it tricky
A Mask one token, positive logits only baseline sanity check
B Mask a token that had a negative logit the "multiply-by-0" trap
C All-but-one tokens masked degenerate: forced token, prob
D All tokens masked degenerate: division by zero — must detect
E Zero tokens masked constraint is a no-op; softmax unchanged
F Finite vs literal limiting behaviour, why NaN is avoided
G Multi-step grammar (JSON prefix) valid set changes each step
H Word problem: refund guardrail reactive filter, not a mask
I Exam twist: multiplicative mask boosts a bad token proving the parent's warning numerically

Example A — mask one token, all logits positive (Cell A)

Forecast: guess first — does { become the most likely token now that 9 is gone?

  1. Apply the mask. Set , so the biased logits are . Why this step? Adding to the forbidden logit makes .
  2. Sum the survivors. Denominator . Why this step? Softmax's denominator only counts allowed tokens now, so probabilities renormalize automatically.
  3. Divide. , , . Why this step? This is softmax applied to the two survivors.

Verify: ✓ and the highest-logit allowed token { won, as expected.


Example B — masking a token that had a negative logit (Cell B)

Forecast: b already had a low logit — does masking it barely change anything?

  1. Additive mask. Biased logits , so , . Why this step? kills b completely regardless of how negative it already was.
  2. Compare to no mask. Un-masked: . Why this step? Shows masking pushed a from ; it removed b's leftover .

Verify: masked probs sum to ✓, and a strictly increased ✓ — masking never lowers an allowed token's probability.


Example C — everything masked but one (Cell C: forced token)

Forecast: does the model's preference (token 0 had the biggest logit) still matter?

  1. Mask. Biased . Why this step? Two tokens forbidden, one allowed.
  2. Softmax over one survivor. . Why this step? A single survivor always gets probability — the model is forced, its original logits are irrelevant.

Verify: for the allowed token, for the others; sum ✓. This is exactly what happens deep inside a JSON string when only a closing quote is legal.


Example D — everything masked (Cell D: the degenerate crash)

Forecast: what is ?

  1. Mask. Biased , so numerators and denominator are all . Why this step? Every .
  2. Attempt softmax. undefined (NaN). Why this step? The denominator is ; division by zero is not a probability.
  3. The fix. A correct decoder must detect an empty valid set and either raise an error or backtrack the grammar. Never feed an all-masked vector to softmax. Why this step? This case means your grammar has a dead-end; masking cannot invent a legal token.

Verify: denominator , so no valid distribution exists ✓ — the machine-check below confirms the sum of masked exponentials is exactly .


Example E — no tokens masked (Cell E: no-op constraint)

Forecast: is a "constraint that allows everything" the same as no constraint?

  1. Mask. , biased logits unchanged: . Why this step? Adding does nothing.
  2. Softmax. ; . Why this step? Identical to plain sampling.

Verify: ✓. An all-allowed mask is provably a no-op — useful for testing your masking code by feeding it a trivial constraint. See 4.3.4-Sampling-strategies for how temperature would further reshape this.


Example F — versus literal (Cell F: limiting behaviour)

Forecast: does using a big finite number instead of leak any probability to the bad token?

  1. True mask. exactly.
  2. Practical mask. Biased bad logit . Then underflows to in floating point. Why this step? Any input below about makes round to in double precision, so behaves like but never produces 's indeterminate forms.
  3. Result. , . Why this step? Numerically identical to the true mask, but safe from inf - inf = NaN if two masked logits were ever subtracted.

Verify: evaluates to and ✓.


Example G — multi-step JSON grammar, the valid set moves (Cell G)

Forecast: at the very start, is 5 ever a legal first character?

Look at the figure: each state of the little parser lights up a different allowed set (the red tokens are the only ones with mask bias ).

Figure — Guardrails and constrained generation
  1. Step 1 — nothing written. Legal first char of a JSON object: only {. Mask everything else. Why this step? A JSON value here must open an object; a bare 5 would be invalid at position 0.
  2. Step 2 — prefix {. Legal next: a " (start a key) or } (empty object). Digits still forbidden. Why this step? The grammar parser reads the open brace and knows only keys or a close can follow.
  3. Step 3 — prefix {"r":. Now, because the schema says r is an integer , legal next tokens are the digits 15 only. Here 5 finally becomes allowed. Why this step? The valid set is prefix-dependent — this is the whole point of constrained decoding versus a fixed blacklist.
  4. Step 4 — prefix {"r": 5. Legal next: } to close. Digits now forbidden again (single-digit schema). Why this step? The state machine has consumed one integer and expects the object to close.

Verify: the character 5 has mask bias at steps 1–2 and at step 3 — the same token flips legality as the prefix grows, which no static rule could capture. This is why 6.2.8-Function-calling-and-tool-use relies on schema-driven decoding.


Example H — word problem: refund guardrail (Cell H, reactive)

Forecast: will a simple keyword rule catch this? Would it catch "I'll make sure you're compensated"?

  1. Regex pass. Pattern \b(refund|money back|reimburse)\b matches refund1 match. Why this step? Fast, cheap first line of defence — catches the explicit word.
  2. Guardrail fires. Since match count , replace the response with the fallback: "I'll escalate this to a human agent." Why this step? This is reactive filtering (generate → check → replace), not masking — the model already produced the sentence.
  3. Edge case. "I'll make sure you're compensated" has 0 regex matches, so it slips through; you would add an LM-as-judge from 5.1.5-RLHF-and-preference-learning-trained models to catch the paraphrase. Why this step? Guardrails handle fuzzy meaning; constrained generation cannot, because "promising a refund" is not a formal grammar.

Verify: regex match count on the first sentence (fires) and on the paraphrase (misses) ✓ — demonstrating why guardrails are probabilistic, unlike the guaranteed validity of masking. See also 7.3.2-Adversarial-attacks-on-LLMs for how attackers phrase around keyword rules, and 6.2.11-Multi-agent-systems for a separate judge agent.


Example I — exam twist: multiplicative masking boosts a forbidden token (Cell I)

Forecast: the parent note warned about this — by how much does BAD go up?

  1. Correct additive mask (reference). Biased , . Why this step? Establishes the correct target: a truly forbidden token must reach probability exactly , so we can measure how badly the wrong method deviates from it.
  2. Student's multiplicative mask. Multiply BAD's logit by : it becomes , not . New logits . Why this step? Multiplying by sets the logit to , but a logit of is a perfectly ordinary, non-forbidding value.
  3. Softmax. . Why this step? Compare to BAD's original probability .
  4. The disaster. The forbidden token went from (allowed model) to — a 5.7× increase — because multiplying a negative logit by raises it toward . Why this step? This is the concrete proof behind the parent's mistake callout: multiplicative masking can promote exactly the tokens you meant to ban.

Verify: multiplicative-mask original ✓; additive-mask ✓.


Recall Self-test

Why must an all-masked logit vector be caught before softmax? ::: The denominator becomes , so softmax returns NaN; the grammar has a dead-end and no legal token exists. In Example G, why is 5 illegal at step 1 but legal at step 3? ::: The valid token set is prefix-dependent; only after {"r": does the schema's integer field open, so digit 5 earns mask bias . Multiplying logit by gives . Is a "forbidding" value? ::: No — a logit of yields positive probability after softmax and here boosts the token, which is exactly the multiplicative-mask trap.