Deterministic LLM Guardrail Implementation by Maxim KushnirDeterministic LLM Guardrail Implementation by Maxim Kushnir

Deterministic LLM Guardrail Implementation

Maxim Kushnir

Maxim Kushnir

Deterministic AI guardrail

Turn a non-deterministic LLM into a deterministic, schema-validated, auditable component.

About this project. A standalone, generalized extraction of an architecture pattern from Ventute — a production AI-driven business-simulation platform — distilled into self-contained, runnable form. Published as a portfolio piece demonstrating production-grade LLM guardrails and typed structured-output validation. Author: @m4xkushnir.

A small, dependency-light reference implementation of the boundary that sits between a Large Language Model and your application state. The model proposes; this layer disposes. Same state in, same state out — every time — regardless of how creatively the model misbehaves.The problem: non-deterministic models corrupting deterministic systems
An LLM is a probabilistic text generator. Point it at a database, a state machine, or a ledger and you have wired a random number generator directly into your source of truth. In production the failure modes are not exotic — they are routine:
Failure shape What the model does What it costs you Malformed Wraps JSON in ```json, buries it in prose, truncates mid-field Parse exception → dropped turn, or worse, a partial write Missing Omits a required field KeyError deep in your business logic Invalid enum Invents an action ("self_destruct") that isn't in your set Dispatch to a code path that doesn't exist Out of range Emits confidence: 1.7, priority: 99 Silent logic corruption downstream Exaggerated Proposes multiplying a value by 1,000,000 in one step Runaway, un-auditable state Injected keys Adds __proto__ / unexpected fields Prototype pollution, un-vetted data persisted
Catching these ad-hoc — a try/except here, a .get() with a default there — scatters trust decisions across your codebase and guarantees that one of them is missing. The result is intermittent, hard-to-reproduce state corruption that only shows up under the exact model output nobody tested.

The solution: a strict JSON boundary with a deterministic validation layer

Treat the model as an untrusted narrator: it may propose structure and numbers, but it authors nothing your system is obliged to trust. Every proposal crosses a single, explicit boundary made of three composable layers, and the application — never the model — has the last word.

Design notes

Zero domain business logic. The example domain is a generic Agent that proposes an action, a status, and numeric resource_deltas against a shared State. Replace AgentDirective, CLAMP_RANGES, and MAX_GROWTH_FACTOR with your own; the parser, wrapper, and retry/fallback machinery are unchanged.
Provider-agnostic. The guardrail depends only on a tiny LLMClient protocol (complete(system_prompt, user_prompt) -> str). Drop in any OpenAI / Anthropic / OpenRouter client that satisfies it. The bundled ScriptedLLM replays canned responses so the whole suite runs with no API key and no network.
Purity where it counts. Layers 1 and 3 are pure functions; Layer 2 is a declarative schema. The only non-determinism in the system is the model call itself, and it is boxed in on all sides.
Like this project

Posted Jul 31, 2026

Implemented a deterministic boundary for LLM proposals to prevent state corruption.