13 · Writing a guardrail from a rule in prose¶
Guardrail Writing turns a rule you can say into a policy you can ship, with the evidence behind it. You state the rule in your own words and name the obligation it must meet; the service asks your model, on your key, to draft the policy, compiles the draft, verifies the obligation on it, and repairs it where it leaks. This chapter says how a request is made, what comes back, and what a generated policy is and is not.
The request¶
A guardrail request carries generation in place of a policy. The obligation is yours, in operands,
and never the model's to choose:
result = client.generate_and_verify(
"Funds may move only for a signed-in user whose account is not frozen.",
"execute_transfer_funds", # the action the rule is about
"is_authenticated", # the context it must never fire without
provider="anthropic", model="claude-sonnet-5-5",
api_key_env="ANTHROPIC_API_KEY", # your key, sent with this one request and never stored
rounds=3,
)
print(result.outcome["verdict_produced"], result.status)
for checked in result.envelope["obligations_verified"]: # a list, so its shortness is visible
print(checked["round"], checked["action"], checked["context"], checked["status"])
print("ship it" if result.shipping_candidate else "nothing verified to ship")
| Field | What it is |
|---|---|
provider, model |
anthropic, gemini or openai_compatible, and the model's name as that provider spells it |
api_key |
your key for that provider. Required: the service holds no model credential of its own, and has none to fall back to |
prompt |
the rule in your words, at most 8 KB |
rounds |
1 to 3, and your plan may allow fewer (chapter 40): 1 drafts and verifies once; 2 or 3 hand the solver's repair back to your model for a redraft while the obligation still fails |
dialect |
the draft is written in Colang 2 unless you ask for Colang 1 |
base_policy |
an existing file the rule is to be added to |
base_url |
for an OpenAI-compatible provider, your own gateway, where your plan includes one and the host is on your tenant's list |
cases |
at most 16 situations stated in your terms, each with what must happen: blocked or allowed. Each is solved on the policy to ship |
What happens, one round¶
- Your model is asked for a draft, with your key, held only in memory for the call.
- The draft is compiled. A draft the compiler did not fully understand gets no verdict. A missing
rule reads as a pass, and a fluent draft is both the likeliest to hide one and the likeliest to be
trusted anyway. So a draft with any parse diagnostic is void:
outcome.verdict_producedisfalse, the lines the compiler could not read are quoted with their numbers, and no solve runs. The job still succeeds, because the answer is real: your draft does not say what you think. - A draft that never mentions the action or the context fails as
obligation_absent: a bypass by omission. - Otherwise the obligation is verified on the draft, under
patchunless you name a profile, and a failing obligation comes back with the solver's repair.
Rounds two and three go round again only while the obligation still fails and the solver has a repair to hand back. The prompt then holds the previous draft, the verdict and the solver's guard, in the draft's own names, with an instruction to change nothing else. The obligation never changes between rounds: verifying a different question would be the loop marking its own work. A round that does not compile stops the loop, and a worse round is reported as worse.
What comes back¶
The response leads with outcome: whether a verdict was produced, a sentence saying what happened, your
cases blocked and allowed, and the shipping_candidate, the file to ship. It is chosen in this order:
- the first round's draft the verifier could not break;
- otherwise, the solver's own repaired policy from the best round;
- otherwise, nothing.
That order is the point of the feature. When your model and the solver's repair disagree, the repair ships, because it is the one with a proof attached, and it carries the over-blocking check a redraft does not.
The evidence follows: obligations_verified (a list, so that its shortness is visible), the verdict, the
certificate, and the full account under generation: each round's draft as an artefact, its verdict and
diagnostics, why the loop stopped, and the model calls made, with the tokens your provider reported. Every
artefact records which side produced it (produced_by): the model, or the solver.
Your key and your prompt¶
The service keeps neither. Your key and your prompt travel from the edge to the worker in one value of the service's private store, read and deleted in one operation, and expire with the job's queue. They are never written to a database, an object store, a log or an event, and while a call is in flight your key is registered with the log filter, which replaces it wherever it appears, including in an error a provider echoes back. The job keeps the prompt's SHA-256 and its length; no raw model response is stored. The draft is kept, as a policy file under your retention, because it is the product (chapter 16).
Two consequences you will meet:
- If a worker stops mid-job and the job is retried, the key and prompt are gone: the job fails as
generation_input_expired, is not billed, and you submit it again with a new idempotency key. - A request whose handoff store cannot be reached is refused with
503andRetry-After, rather than the key being kept somewhere durable.
A policy in Colang 2¶
Drafts are written in Colang 2 by default, and verification reads a declared subset of it. A construct
outside the subset is not approximated: a job over a policy that uses one inside the question's reach is
refused as colang2_construct_unsupported, naming the line (chapter 6). For
everything else Colang can express, see the NeMo Guardrails documentation; this guide documents what the
verifier compiles.
What a generated policy is and is not¶
The draft is the model's; the verdict, the certificate and the repair are the solver's. Every response
says so, in its standing caveat draft_is_the_models, and every artefact names who produced it.
- It is not a correct policy. One named obligation was verified over one draft, and
obligations_verifiedlists exactly that. Every response carries the caveatverified_only_as_asked. An obligation the rule requires and the draft never mentions has no variable and no row, and nothing here finds it. - It is not a guardrail an AI verified. A model wrote text; the solver verified one proposition about that text. The response never calls a draft verified beyond the obligations it lists.
- It is a starting point with evidence. A draft that verifies is a policy that meets the obligation you named, with a certificate anyone can check. Review it as you would review a colleague's policy, and add the rest of your rules as questions of their own (chapter 4).
Whether your model accepts a rule written in another language, Portuguese for instance, is your model's matter: the service passes your prompt as you wrote it, and claims nothing about how a model reads it.
The limits¶
- A generated policy is verified for the obligations listed in
obligations_verified, and for nothing else. - The draft's quality is your model's. The service checks it and repairs it; it does not make the model right.
- A void draft has no verdict. Do not ship it, and do not quote an earlier round's verdict for it.