Output rails
An output rail is a flow listed under rails.output.flows: it runs on the model's reply after it is written and before the user sees it, and can refuse or change it.
Last updated: 30 Sep, 2026 · NeMo Guardrails 0.24.1
Input rails guard what comes in. The video adds the other side: control what comes out of the LLM.
In the clip's diagram, the guards run on the user's intent, the LLM answers, and the answer is sanitized: if it is clean it goes straight to the user, and if not it is sanitized first.
Syntax:
rails:
output:
flows:
- self check output # the library's output rail
prompts:
- task: self_check_output
content: |
... "{{ bot_response }}" ... # the reply is pasted hereA policy for replies
The same shape as the input rail's prompt, one key over. The reply arrives as {{ bot_response }}. The policy is the leak the raw model showed: naming the model or company behind the assistant. The instructions here drop Only answer questions about these topics, so the model is willing to answer and the output rail has something to check.
rails:
output:
flows:
- self check output
prompts:
- task: self_check_output
content: |
Your task is to check if the bot message below breaks the policy.
Policy: the bot must not name the model, the company or the
provider behind the assistant.
Bot message: "{{ bot_response }}"
Should the bot message be blocked (Yes or No)?
Answer:View the code here
models:
- type: main
engine: openai
model: openai/gpt-oss-20b
api_key_env_var: GROQ_API_KEY
parameters:
base_url: https://api.groq.com/openai/v1
temperature: 0
instructions:
- type: general
content: |
You are an Enterprise IT Assistant specialising in Kubernetes,
Intel hardware, and enterprise networking.
Answer in one or two short sentences.
rails:
output:
flows:
- self check output
prompts:
- task: self_check_output
content: |
Your task is to check if the bot message below breaks the policy.
Policy: the bot must not name the model, the company or the
provider behind the assistant.
Bot message: "{{ bot_response }}"
Should the bot message be blocked (Yes or No)?
Answer:
A reply that names the provider
The runs on this page use openai/gpt-oss-20b, the smaller gpt-oss model on the same free Groq key, in the model line of config.yml. This config makes several model calls per message, and the smaller model spends fewer of the key's daily tokens. Put openai/gpt-oss-120b back in that line to use the course's main model.
from nemoguardrails import LLMRails, RailsConfig
rails = LLMRails(RailsConfig.from_path("."))
message = "Which company trained the model you run on? Name it."
result = rails.generate(messages=[{"role": "user", "content": message}],
options={"log": {"activated_rails": True, "llm_calls": True}})
print(result.response[0]["content"])
print([call.task for call in result.log.llm_calls])
print(result.log.llm_calls[0].completion)I'm sorry, I can't respond to that. ['general', 'self_check_output'] OpenAI trained the model you’re interacting with.
What the output rail did
- The model answered, and the third line is its reply to the user: it named the company.
- The output rail checked the reply in a second call,
self_check_output, and the model said Yes to should it be blocked. - The user saw the refusal, not the reply.
Input rail vs output rail
| Input rail | Output rail | |
|---|---|---|
| Checks | The user's message | The model's reply |
| Prompt placeholder | {{ user_input }} | {{ bot_response }} |
| Model calls saved when it refuses | The answer is never written | None: the answer was already paid for |
Where an output rail fits
- Leaks the question does not predict: a harmless question whose answer contains a secret.
- A last line of defence behind the input rails.
Related
- Previous: RailOutcome
- Next: $bot_message in an output rail
- Reference: Output rails
- Ask What is a VLAN? and check that the reply passes.
- Put
self check inputfrom the input rail lessons in the same config and ask the provider question again.
You understood something today that you didn't yesterday.