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 →

@action and register_action

An action is a Python function decorated with @action that a Colang flow runs with execute; register_action attaches it to an LLMRails object so the flow can find it.

Last updated: 30 Sep, 2026 · NeMo Guardrails 0.24.1

Every rail so far asked a model. Some checks need no model: an email address or an API token has a shape a regular expression can find. The video writes those checks as Python actions.

Syntax:

python
@action(is_system_action=True)          # mark the function as an action
async def detect_pii_in_input(context=None):
    ...                                  # return anything the flow can test

rails.register_action(detect_pii_in_input)   # the flow calls it by this name

The PII action from the video

The video's detect_pii_in_input, unchanged: five patterns, and the list of the ones found. An empty list means clean, and is falsy in the flow. Save it as actions.py.

python
import re
from typing import Optional

from nemoguardrails.actions import action


@action(is_system_action=True)
async def detect_pii_in_input(context: Optional[dict] = None):
    """Returns list of PII types found, or empty list (falsy) if clean."""
    user_message = context.get("user_message", "") if context else ""

    patterns = {
        "email":       r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b",
        "phone":       r"\b(\+\d{1,2}\s?)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}\b",
        "ssn":         r"\b\d{3}-\d{2}-\d{4}\b",
        "api_key":     r"(api[_\s-]?key|token|secret)[:\s]+[A-Za-z0-9_\-]{10,}",
        "credit_card": r"\b\d{4}[\s-]\d{4}[\s-]\d{4}[\s-]\d{4}\b",
    }
    found = [ptype for ptype, pat in patterns.items()
             if re.search(pat, user_message, re.IGNORECASE)]
    return found  # empty list = no PII = falsy

The flow that calls it

$pii_found = execute detect_pii_in_input runs the action and keeps its result. The if reads it like Python.

text
define bot ask to remove pii
  "I noticed your message may contain sensitive information (email, phone, API key, etc.). Please remove any personal or secret data before sending — I don't store sensitive details!"

define flow check input for pii
  $pii_found = execute detect_pii_in_input
  if $pii_found
    bot ask to remove pii
    stop

Running it on every message

yaml
rails:
  input:
    flows:
      - check input for pii
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-120b
    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.
      Only answer questions about these topics.
      Answer in one or two short sentences.

rails:
  input:
    flows:
      - check input for pii

The video's PII messages

ExampleAPI keyFrom the video, run on Groq
from actions import detect_pii_in_input
from nemoguardrails import LLMRails, RailsConfig

rails = LLMRails(RailsConfig.from_path("."))
rails.register_action(detect_pii_in_input)


def chat(message):
    reply = rails.generate(messages=[{"role": "user", "content": message}])
    print("User:", message)
    print("Bot :", reply["content"])

chat("My email is john.doe@company.com, help me set up Kubernetes RBAC")
chat("My API token is token:xK9mL3vQ2nR8pT5w, is this safe in a ConfigMap?")
chat("How do I set up Kubernetes RBAC?")

What the action did

  • The email and the API token were refused by your own words, and no model was called for them: the action ran, the flow stopped.
  • The clean question passed the check and went on to the model.
  • register_action(detect_pii_in_input) used the function's name, which is the name the flow calls.

The video writes its messages with a long dash after the email address; this page uses a comma. The pattern matches either way.

A regex action vs a self-check rail

Regex actionself check input
CostNo model callOne model call
CatchesExact shapes: email, SSN, tokensMeaning: jailbreaks, provider questions
MissesAnything not in a patternMay miss an exact token

Where you write actions

  • Personal data, lengths and formats, before any model call.
  • Lookups in your own systems, as the Execution rails lesson does with tickets.
Watch out. Actions are async. The runtime awaits them, so a slow call inside, such as an HTTP request to a moderation service, belongs there; a blocking time.sleep or a sync client stalls every conversation.
Try it yourself
  • Send the video's My SSN number is 123-45-6789 and check which pattern found it.
  • Remove the register_action line and read the reply.

Slow is fine. Stopping is the only problem.