@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
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 policyThe 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.
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.
rails:
input:
flows:
- check lengthYour own flow goes in the same list as the library's. The runtime does not distinguish between them.
The action
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.
execute namein Colang runs a Python action.@action(name=...)names it andregister_actionattaches it.- An action is awaited, so it may do slow work.
- Change 40 to 400 and run the same two messages.
- Return a string from the action instead of a boolean and see how the
ifbehaves. - Put
check lengthafterself check inputin the list and think about which order you want.
Little by little, you're building something great.