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

@action and register_action

Every flow so far has either said something or stopped. execute is the Colang statement that runs your Python, and the function it runs is called an action. This is where a rail stops being configuration and starts being code.

The rail, in Colang

text
define user ask refund policy
  "How long does a refund take?"
  "When do I get my money back?"

define bot give refund policy
  "Refunds take five working days, once the parcel is back with us."

define flow refund policy
  user ask refund policy
  bot give refund policy

The refund flow from lesson 8, unchanged. Below it goes the new part: a bot intent for the refusal, and a flow that runs a Python function and reads what it returned.

text
define bot too long
  "That message is longer than I can read. Please shorten it."

define flow check length
  $ok = execute check_length
  if not $ok
    bot too long
    stop

$ok = execute check_length runs an action and puts what it returned into a variable. The if reads it like ordinary Python, and stop ends the turn as it did in lesson 14.

yaml
rails:
  input:
    flows:
      - check length

Your own flow goes in the same list as the library's. The runtime does not distinguish between them.

The action

Example
import pretend_nemo
from nemoguardrails import LLMRails, RailsConfig
from nemoguardrails.actions import action


@action(name="check_length")
async def check_length(context: dict):
    return len(context.get("user_message", "")) <= 40


rails = LLMRails(RailsConfig.from_path("."))
rails.register_action(check_length, "check_length")
for ask in ["Where is my order?", "I would very much like to know where my parcel is"]:
    print(rails.generate(messages=[{"role": "user", "content": ask}])["content"])

Two things register the function. @action marks it as an action and gives it the name Colang will use; register_action attaches it to this particular LLMRails. Lesson 27 moves both into a config.py so the folder carries them.

The action is async. The runtime awaits it, so anything that would block, such as an HTTP call to a moderation service, belongs here rather than in the flow.

A rail that costs nothing

This one never calls a model. That matters more than it looks: a length check, a regular expression or a lookup in a set runs in microseconds, and putting it first in rails.input.flows means the expensive check behind it never sees the messages the cheap one has already rejected.

Worth remembering
  • execute name in Colang runs a Python action.
  • @action(name=...) names it and register_action attaches it.
  • An action is awaited, so it may do slow work.
Try it yourself
  • Change 40 to 400 and run the same two messages.
  • Return a string from the action instead of a boolean and see how the if behaves.
  • Put check length after self check input in the list and think about which order you want.

Little by little, you're building something great.