Guardrails and AI Safety

Guardrails and AI Safety

Output Guardrails — Patterns

Output guardrails › Patterns

7 of 57 in this book · 3 min read

Hold a retractable tail

Problem. Buffering the whole response removes the progressive display that makes a slow model tolerable. Streaming it unchecked means a violation can be fully read before any verdict exists. Both choices are made once, for every response, and neither is acceptable.

Approach. Stream continuously but keep a trailing window unsent — a sentence, a paragraph, a fixed token count — and run the check on each completed unit before the window advances. The reader sees steady progress while the system always holds back more than it has committed to, and a violation found in the window is removed before anything about it was displayed. Size the window from where violations actually appear in your traffic, not from intuition.

Trade-off. Perceived latency rises by the length of the window, and a violation whose meaning only becomes clear across several paragraphs still escapes. Some content types, notably code and tables, have no safe unit boundary smaller than the whole artifact, so the pattern degrades to buffering for exactly the outputs that are longest.

Feed the verdict back once, then stop

Problem. Discarding a violating response and sampling again is the obvious remedy and the expensive one. The naive loop re-rolls from the same context, which reproduces the same violation at roughly the same rate, and the user waits through every attempt.

Approach. Allow exactly one retry, and make it informed: pass the specific finding back as an additional constraint so the second attempt is a different generation rather than another draw from the same distribution. If the second attempt also fails, stop and return the prepared response for that situation. Record the first-attempt violation rate and the rescue rate of the retry separately, because a retry that rarely rescues is a latency cost with no benefit.

Trade-off. The constraint fed back describes the violation, which means the model is now being told, in the same context, exactly what the system is checking for. On a context that was adversarially constructed in the first place, that is a hint you have just handed over.

Sanitise for the renderer, separately from judging the content

Problem. Content checks ask whether the text is acceptable. They do not ask what the text will do when a renderer interprets it, and the dangerous behaviours — a remote image fetched on display, a link whose target differs from its label, markup that escapes its container — are properties of the rendering, not of the meaning.

Approach. Treat every generated artifact as untrusted markup and pass it through a stage whose only job is the target surface: an allowlist of elements and attributes, a policy for external references, and an explicit decision about whether anything may be fetched automatically. Run it on every path that renders output, including notifications and exports, and choose the policy from what the renderer supports rather than from what the model usually produces.

Trade-off. The sanitiser degrades legitimate rich output — a diagram, an embedded chart, a reference to an internal asset — and the user sees a product that formats worse than the model can. Maintaining a separate policy per surface is also a standing cost that nobody owns until the first surface renders something the others blocked.

Attach the verdict to the artifact, not to the channel

Problem. Enforcement implemented inside the chat handler protects the chat. The same generation is then exported, emailed, replayed into an evaluation set, and fetched by an integration, and each of those paths is a separate opportunity for someone to forget.

Approach. Make the check a property of the generation record: inspect once, store the verdict and its provenance alongside the text, and have every consumer read the verdict rather than the text. Consumers encountering a record with no verdict refuse to emit it. New surfaces then inherit enforcement by construction, and the number of places that may call the checker stays at one.

Trade-off. A stored verdict is a snapshot of a policy that will change, so old records carry judgements made under rules that no longer apply, and re-evaluating a corpus of them is expensive. It also centralises a dependency: a consumer that cannot read the verdict store cannot emit anything, which turns a storage incident into a product-wide outage.