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

rails.output.flows

An input rail checks the message on the way in. An output rail checks the reply after it has been written and before it reaches the user, which is the last chance to stop something going out.

self check output

yaml
rails:
  output:
    flows:
      - self check output
prompts:
  - task: self_check_output
    content: |
      Blocked topics: staff20
      Message: "{{ bot_response }}"
      Answer Yes if the message is about a blocked topic, No otherwise.

The same shape as self check input in lesson 13, one key over. rails.output.flows lists the flows to run on every reply, and the self_check_output task gets the reply as {{ bot_response }} instead of the user's message. The flow reads the model's answer with the rule from lesson 14: Yes blocks, No allows.

A reply that is stopped

Example
import pretend_nemo
from nemoguardrails import LLMRails, RailsConfig

rails = LLMRails(RailsConfig.from_path("."))
rails.llm.say('Bot message: "Use code STAFF20 for a staff price."', task="generate_bot_message")
result = rails.generate(messages=[{"role": "user", "content": "Any deals for staff?"}], options={"log": {"activated_rails": True, "llm_calls": True}})
print(result.response[0]["content"])
for rail in result.log.activated_rails:
    print(rail.type, "|", rail.name, "| stop:", rail.stop)

say made the model write a reply that gives away a staff code. The runtime produced that reply through the three dialog steps from lesson 12, then ran the output rail on it, and the rail stopped the turn. The user saw the refusal, not the code.

What an output rail costs

Example
import pretend_nemo
from nemoguardrails import LLMRails, RailsConfig

rails = LLMRails(RailsConfig.from_path("."))
rails.llm.say('Bot message: "Paris is the capital of France."', task="generate_bot_message")
result = rails.generate(messages=[{"role": "user", "content": "What is the capital of France?"}], options={"log": {"activated_rails": True, "llm_calls": True}})
print(result.response[0]["content"])
print([call.task for call in result.log.llm_calls])

A harmless reply goes through, and the log shows why it costs more than it did: a fourth model call, self_check_output, after the three that wrote the reply. Every reply checked this way is one more request to the model.

Try it yourself
  • Change the say sentence so it mentions staff20 in lower case and run the first example again.
  • Put self check input from lesson 13 in the same config and count the model calls for one message.
  • Print rails.llm.prompts[-1] and find where the reply was pasted into the prompt.

Little by little, you're building something great.