NeMo Guardrailsnemoguardrails 0.24.1 · Python 3.10+
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
37 small wins to finish your pathNext lesson →

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.

Output rails · from The Complete AI Security Course In 8 Hours · 35:29 to 36:18

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:

yaml
rails:
  output:
    flows:
      - self check output            # the library's output rail
prompts:
  - task: self_check_output
    content: |
      ... "{{ bot_response }}" ...   # the reply is pasted here

A 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.

yaml
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:
Project files used on this pageThis lesson builds on a project from earlier lessons. The code below imports this file. Click a file to see its code, or follow the link to the lesson that wrote it. To run the code yourself, keep it in the same folder.
View the code here
config.yml
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.

ExampleAPI key
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)

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 railOutput rail
ChecksThe user's messageThe model's reply
Prompt placeholder{{ user_input }}{{ bot_response }}
Model calls saved when it refusesThe answer is never writtenNone: 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.
Watch out. Every reply checked this way costs one more model call. An output rail that runs a regex, as in the next lesson, costs nothing.
Try it yourself
  • Ask What is a VLAN? and check that the reply passes.
  • Put self check input from the input rail lessons in the same config and ask the provider question again.
PreviousRailOutcome

You understood something today that you didn't yesterday.