Version: Latest

Creating NLU-only CALM bots

Creating an NLU-only CALM bot is a powerful approach that enables you to leverage your existing NLU pipeline without relying on large language models (LLMs). This guide will show you step-by-step how to use your NLU pipeline with the new Flows primitive in Rasa Pro CALM. This is particularly helpful if you lack access to LLMs or want to transition your Story-based bots to align better with newer conversational AI technologies.

Setting Up Your NLU Pipeline for CALM

To create an NLU-only CALM bot, you need to configure your NLUCommandAdapter and integrate it with the FlowPolicy in your config.yml. Below, you'll learn how to accomplish this.

Adding the NLUCommandAdapter to Your Configuration

To translate your NLU pipeline output into something your CALM bot can understand, you need the NLUCommandAdapter and the FlowPolicy. The NLUCommandAdapter converts NLU intents and entities into commands that the FlowPolicy uses.

Add the NLUCommandAdapter to your config.yml like this:

config.yml
recipe: default.v1
language: en
pipeline:
- name: WhitespaceTokenizer
- name: LanguageModelFeaturizer
model_name: "bert"
model_weights: "sentence-transformers/all-MiniLM-L6-v2"
- name: "CRFEntityExtractor"
- name: LexicalSyntacticFeaturizer
- name: CountVectorsFeaturizer
- name: LogisticRegressionClassifier
- name: EntitySynonymMapper
- name: DucklingEntityExtractor
# url of the running duckling server
url: "http://0.0.0.0:8000"
dimensions: ["time", "number", "amount-of-money", "distance", "email"]
locale: "en_US"
timezone: "US/Eastern"
timeout : 3
- name: NLUCommandAdapter
policies:
- name: FlowPolicy

In the configuration above, the NLUCommandAdapter is added at the end of your NLU pipeline to bridge the gap between intent/entity prediction and flow execution.

Defining Flows with NLU Triggers

With your NLU pipeline set up, the next step is to define flows that can be triggered by specific NLU intents. Here’s how to configure NLU triggers in flows.yml.

Define the Flows with NLU triggers

The NLUCommandAdapter uses predicted intents to start a flow. To trigger a flow using an intent, add an nlu_trigger to your flow definition.

Below is an example flow with an NLU trigger:

flows.yml
flows:
my_flow:
description: "A flow triggered with <intent-name>"
nlu_trigger:
- intent:
name: <intent-name>
confidence_threshold: 0.5 # threshold value, optional
steps:
- action: my_action

It is recommended to use a confidence_threshold to avoid accidental flow triggers due to low-confidence predictions.

info

Even though this is an NLU-only approach, not all of your flows need to have an NLU trigger. For example, you could create some flows that are only startable via call or link steps. This gives you the control over which flows can be started by the user and which ones are only started by the bot.

Multiple Intents in NLU Trigger

If you want a flow to be triggered by multiple intents, list them under nlu_trigger like this:

flows.yml
flows:
multi_intent_flow:
description: "Flow that can be triggered by multiple intents"
nlu_trigger:
- intent:
name: intent_one
confidence_threshold: 0.6
- intent:
name: intent_two
confidence_threshold: 0.7
steps:
- action: my_action

This configuration allows you to handle similar user queries by triggering the same flow for different intents.

Adding NLU Triggers to Default Patterns

To manage flow cancellations or specific fallback scenarios effectively, consider incorporating NLU triggers into CALM's default patterns. For instance, you can define a cancel intent with example phrases such as 'cancel', 'stop', 'nevermind', etc., and associate it with pattern_cancel_flow. This approach ensures a more seamless conversation flow by preemptively handling user interruptions or requests to terminate a dialogue.

patterns.yml
pattern_cancel_flow:
description: Conversation repair flow that starts when a flow is cancelled
name: pattern_cancel_flow
nlu_trigger:
- intent:
name: cancel
confidence_threshold: 0.5
steps:
- action: action_cancel_flow
- action: action_clean_stack
- action: utter_flow_cancelled_rasa
info

To keep your configuration organized, it is best practice to store your customized patterns in a separate file from your main flows. This way, you can easily distinguish between patterns and flows.

Check the Default Pattern Configuration for reference when modifying the default patterns and responses.

Best Practices for using slots

Slots play a crucial role in guiding the conversation logic of a CALM bot. Here are some best practices for effectively using slots in your flows.

Branching Based on Slot Values

Use slots to store information such as user preferences, choices, or other relevant data. Then, use slot values in conditions to determine the flow of your conversation. Slots help reduce complexity by eliminating the need for multiple stories and (often) custom actions.

Use active_flow in Mapping Conditions

When using the same entity to fill multiple slots across different flows, leverage the active_flow attribute in mapping conditions to ensure that the right slot is filled depending on the current active flow.

domain.yml
slots:
location:
type: text
mappings:
- type: from_entity
entity: location
conditions:
- active_flow: booking_flow

Validating Slot Values

Effective slot validation is key to ensuring your bot collects accurate information. Below are ways to validate slot values:

Simple Validation with Slot Rejections

Use slot_rejections to quickly validate values within your flows.

flows.yml
flows:
collect_email:
description: "Flow to collect user email"
steps:
- collect: email
rejections:
- if: not (slots.email matches "^[\w\.-]+@[\w\.-]+\.[a-zA-Z]{2,4}$")
utter: utter_invalid_email

Add a corresponding response to your domain to prompt the user to try again:

domain.yml
responses:
utter_invalid_email:
- text: "The email address seems invalid. Could you provide it again in a valid format?"

Advanced Validation Using Custom Actions

For more complex scenarios, implement custom slot validation within a custom action. For example, you can check if a phone number is already linked to another account.

Create the custom action in actions.py:

actions.py
class ValidatePhoneNumber(Action):
def name(self) -> Text:
return "validate_phone_number"
def run(
self,
dispatcher: CollectingDispatcher,
tracker: Tracker,
domain: DomainDict,
) -> Dict[Text, Any]:
"""Validate slot value."""
phone_number = tracker.get_slot("phone_number")
# Mock API call that returns {"exists": true} or {"exists": false}
response = requests.get(f"https://api.mycompany.com/check/{phone_number}")
account_exists = response.json()["exists"]
if account_exists:
return [SlotSet("phone_number", phone_number)]
else:
return [SlotSet("phone_number", None)]
note

Note this custom action must follow this naming convention: validate_{slot_name} in order for Rasa to call it automatically when the slot is collected.

Summary

In this guide, you learned how to create an NLU-only CALM bot using Rasa, starting with setting up the NLUCommandAdapter in your configuration, defining NLU-triggered flows, using slots for conversation logic, and validating slot values. By following these steps, you can build efficient and flexible assistants that make the best use of your existing NLU resources.

For additional guidance, explore the related topics in Rasa Pro: