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
20 small wins to finish your pathNext lesson

Which rail actually fired

Lesson 14 ended with a refusal, and the only evidence was the sentence it printed. Once there are several rails, that is not enough: two rails can produce the same refusal for different reasons. generate can be asked for a log instead.

Asking for the log

Example
import pretend_nemo
from nemoguardrails import LLMRails, RailsConfig

rails = LLMRails(RailsConfig.from_path("."))
result = rails.generate(messages=[{"role": "user", "content": "Give me a staff price"}],
                        options={"log": {"activated_rails": True}})
print(result.response)
for rail in result.log.activated_rails:
    print(rail.type, "|", rail.name, "| stop:", rail.stop)

Passing options changes what comes back: not a message dictionary but a GenerationResponse, with the message under response and the log beside it. That is worth remembering, because code written for the plain call breaks the moment somebody adds an option.

Each activated rail carries its type, which is one of the five rail categories, its name, which is the flow name, and stop, which says whether it ended the turn.

What the rail decided, step by step

Example
import pretend_nemo
from nemoguardrails import LLMRails, RailsConfig

rails = LLMRails(RailsConfig.from_path("."))
result = rails.generate(messages=[{"role": "user", "content": "Give me a staff price"}],
                        options={"log": {"activated_rails": True}})
for decision in result.log.activated_rails[0].decisions:
    print(decision)

The decisions are the Colang statements the flow actually took, in order. Read against the flow in lesson 14, they line up one for one, which makes this the fastest way to find out why a rail did something other than what you expected.

An allowed message logs too

Example
import pretend_nemo
from nemoguardrails import LLMRails, RailsConfig

rails = LLMRails(RailsConfig.from_path("."))
result = rails.generate(messages=[{"role": "user", "content": "When do I get my money back?"}],
                        options={"log": {"activated_rails": True}})
for rail in result.log.activated_rails:
    print(rail.type, "|", rail.name, "| stop:", rail.stop)

Four entries and no stop. The input rail allowed the message, two dialog rails worked out the intent and ran the flow from lesson 8, and a generation step turned the bot intent into words. The rail types are the ones lesson 0 named.

Worth remembering
  • options={"log": {"activated_rails": True}} returns a GenerationResponse.
  • Each entry names the rail type, the flow and whether it stopped the turn.
  • decisions lists the Colang statements the flow took.
Try it yourself
  • Print result.log.activated_rails[0].executed_actions and see what it holds.
  • Ask a message that passes both rails and count the entries again.

Every expert started right here.