Patterns
Configurations
Default Behavior
Rasa ships a default behavior for every conversation repair case that works out-of-the-box. Each case is handled through a pattern which is a special flow designed specifically to handle the case:
pattern_continue_interrupted
for digressions.pattern_correction
for corrections.pattern_cancel_flow
for cancellations.pattern_skip_question
for skipping collect steps.pattern_chitchat
for chitchat.pattern_completed
for completion.pattern_clarification
for clarification.pattern_internal_error
for internal errors.pattern_cannot_handle
for cannot handle.pattern_human_handoff
for human handoff.pattern_session_start
for procatively starting a session by the assistant.
Voice-specific patterns:
pattern_repeat_bot_messages
for when the user asks the assistant to repeat an utterance.pattern_user_silence
for handling user silences in voice assistants.
The syntax for each of these flows is the same as any other flow.
Conversation repair cases are expected to work out-of-the-box. This means that if the default behaviour is good enough for the assistant's use case, then the flow corresponding to the pattern handling the repair case is not needed in the assistant's project directory.
The Contextual Response Rephraser helps the default responses from patterns to fit in naturally with the context of the conversations.
Modifying default behaviour
It is possible to override the default behaviour of each conversation repair case by creating a flow with the same name as that of the pattern used to handle the corresponding case, like pattern_correction
. If the pattern uses a default action which needs to be modified, you can override the implementation of the default action by implementing a new custom action and use that custom action in the flow.
It is possible to add a link step from a pattern to a flow, except for pattern_internal_error
, where link steps are not allowed.
Additionally, you can link a pattern to the pattern_human_handoff
.
Make sure the assistant is re-trained after the modification is completed.
Since most of these patterns interrupt another flow, they should be kept short and simple.
Sample Configuration
Modify Rasa's response when a flow concludes:
flows:
pattern_completed:
description: Completion of a user's flow
steps:
- action: utter_can_do_something_else
responses:
utter_can_do_something_else:
- text: "Is there anything else I can assist you with?"
Common Modifications
Here are some common modifications to the default behavior.
Requiring Confirmation
You can change the default implementation for a correction and require a confirmation from the user before a slot is updated, e.g. this would result in a conversation like this:
User: I want to send some money to Joe
Bot: How much money do you want to send?
User: 50$
Bot: Do you want to send 50$ to Joe? (Yes/No)
User: Oh wait!! I meant to say to John, not Joe!
Bot: Do you want to update the recipient to John? (Yes/No)
User: Yes!
Bot: Updated recipient to John
Bot: Do you want to send 50$ to John? (Yes/No)
User: ...
To achieve the above confirmation, create a flow named
pattern_correction
which is defined as follows:
flows:
pattern_correction:
description: Confirm a previous correction of a slot value.
steps:
- noop: true
next:
- if: context.is_reset_only
then:
- action: action_correct_flow_slot
next: END
- else: confirm_first
- id: confirm_first
collect: confirm_slot_correction
next:
- if: not slots.confirm_slot_correction
then:
- action: utter_not_corrected_previous_input
next: END
- else:
- action: action_correct_flow_slot
- action: utter_corrected_previous_input
next: END
Also make sure to add the used responses and slots to your domain file:
slots:
confirm_slot_correction:
type: bool
responses:
utter_ask_confirm_slot_correction:
- text: "Do you want to update the {{ context.corrected_slots.keys()|join(', ') }}?"
buttons:
- payload: "yes"
title: "Yes"
- payload: "no"
title: "No, please keep the previous information"
metadata:
rephrase: True
template: jinja
utter_not_corrected_previous_input:
- text: "Ok, I did not correct the previous input."
metadata:
rephrase: True
Implementing a Human Handoff
Currently, the default behaviour for a human handoff is to inform the user that the assistant
cannot help with the request. However, in scenarios where customer service is available,
implementing a human handoff becomes relevant. You can implement a human handoff by writing
a custom action and overriding the flow named pattern_human_handoff
:
flows:
pattern_human_handoff:
description: Human handoff implementation
steps:
- collect: confirm_human_handoff
next:
- if: slots.confirm_human_handoff
then:
- action: action_human_handoff
next: END
- else:
- action: utter_human_handoff_cancelled
next: END
Also make sure to add the used actions, responses and slots to your domain file:
slots:
confirm_human_handoff:
type: bool
mappings:
- type: custom
actions:
- action: action_human_handoff
responses:
utter_ask_confirm_human_handoff:
- text: "Do you want to be connected to a human agent?"
buttons:
- payload: "yes"
title: "Yes"
- payload: "no"
title: "No"
utter_human_handoff_cancelled:
- text: "Ok, I understand you don't want to be connected to a human agent. Is there something else I can help you with?"
metadata:
rephrase: True
React dependent on the current flow
You can change a pattern's behaviour depending
on the flow that was interrupted. This can be done by using the
context
object in the if
condition of a pattern:
flows:
pattern_cancel_flow:
description: A meta flow that's started when a flow is cancelled.
steps:
- id: decide_cancel_step
noop:
- if: context.canceled_name = "transfer money"
then: inform_user
- else: cancel_flow # skips the inform step
- id: inform_user
action: utter_flow_cancelled_rasa
next: cancel_flow
- id: cancel_flow
action: action_cancel_flow
In the above example, the inform_user
step is only used if the flow that
was interrupted is called transfer_money
.
Free form generation for chitchat
By default, chitchat operates via action_trigger_chitchat
that invokes the
IntentlessPolicy to provide a relevant predefined response.
To switch to free-form generated responses, override the default behaviour of pattern_chitchat
by
creating a flow named pattern_chitchat
which is defined as follows:
flows:
pattern_chitchat:
description: handle interactions with the user that are not task-oriented
name: pattern chitchat
steps:
- action: utter_free_chitchat_response
Free-form responses will be generated using an LLM. There's a possibility that the assistant could answer queries outside of the intended domain.
Disabling chitchat
By default, if the Intentless Policy is not configured, the assistant defaults to
the pattern_cannot_handle
, effectively restricting chitchat by informing the user that the request cannot be processed.
To completely restrict casual conversation, override the default behaviour of pattern_chitchat
by
creating a flow named pattern_chitchat
. Instead of the usual behavior that triggers action_trigger_chitchat
,
configure it to use a predefined response:
flows:
pattern_chitchat:
description: |
Handle interactions with the user that
are not task-oriented using a predefined response
name: pattern chitchat
steps:
- action: utter_cannot_handle # or any other response template
Skipping clarification
By default, clarification will check on the users intention by asking the user to choose from a list of flows. In some cases, it may be desirable to skip clarification and move directly to starting a flow by adding a link step directly to that flow.
flows:
pattern_clarification:
description: Conversation repair flow for handling ambiguous requests that could match multiple flows
name: pattern clarification
steps:
- action: action_clarify_flows
next:
- if: context.names contains "<name of a flow>"
then:
- link: <name_of_a_flow>
- else: clarify_options
- id: clarify_options
action: utter_clarification_options_rasa
Preventing multiple Clarifications
By default, clarification will continue to check on the users intentions regardless of the number of times it has asked. This can be avoided by counting the number of clarification requests and linking to the human handoff pattern if this count reaches some threshold.
flows:
pattern_clarification:
description: Conversation repair flow for handling ambiguous requests that could match multiple flows
name: pattern clarification
steps:
- action: action_clarify_flows
- action: action_increase_clarification_count
next:
- if: slots.clarification_count > CLARIFICATION_LIMIT
then:
- link: pattern_human_handoff
- else: clarify_options
- id: clarify_options
action: utter_clarification_options_rasa
This would require the custom action action_increase_clarification_count
to be
implemented
and added to the domain.yml
along with the clarification_count
slot.
Common Voice-specific Pattern Modifications
Handling Call Start and Call Metadata
We have unified the call handling patterns across Voice Channel Connectors, all voice channels handle call starts, ends and metadata in a similar manner.
The assistant will receive the message /session_start
when the call is picked up
along with the call metadata. This intent triggers the Session Start pattern. Here's
a customized pattern that sends utter_greet
when the call connects:
flows:
pattern_session_start:
description: Flow for starting the conversation
name: pattern session start
nlu_trigger:
- intent: session_start
steps:
- action: utter_greet
The following call metadata is received for Twilio:
call_id
is the unique call identifier from Twilio (CallSid
parameter as sent by Twilio Voice)user_phone
is the phone number of the user (Caller
)bot_phone
is the phone number of the bot (Called
)direction
is the call direction. It can be eitherinbound
oroutbound
Action action_session_start
is triggered at the beginning of each Rasa session
and it can be used to set certain slots based on this metadata. These slots can be
used in your utterances for a dynamic greeting. Here is an example:
from rasa_sdk import Action, Tracker
from rasa_sdk.events import SlotSet
from rasa_sdk.executor import CollectingDispatcher
import logging
logger = logging.getLogger(__name__)
class ActionSessionStart(Action):
def name(self) -> str:
return "action_session_start"
def run(self, dispatcher: CollectingDispatcher, tracker: Tracker,
domain: dict) -> list:
# get the call metadata from the tracker
metadata = tracker.get_slot("session_started_metadata")
logger.info(f"🤙 action_session_start's metadata: {metadata}")
# set appropriate slots
if metadata:
return [
SlotSet("user_phone", metadata.get("user_phone")),
SlotSet("bot_phone", metadata.get("bot_phone")),
]
return []
Handling the End of a Call
Call can be ended by the user or by the assistant.
-
When the call is ended by the user
/session_end
message is received by the assistant along with aSessionEnded
event to the conversation. -
Assistant flows can use the default action
action_hangup
to disconnect calls. This action also will add aSessionEnded
event to the conversation.
Using Responses relevant to Voice Channels
It is recommended to use channel specific responses with voice channels. This can be done with channel specific responses:
responses:
utter_setup_guide:
- text: "Click the 👉 button or visit https://example.com/setup-guide"
- text: ""To continue setup, open our website and go to the setup guide""
channel: "twilio_media_streams"
In the above example, note that emoji doesn't translate well to speech. URLs are difficult to conprehend due to the temporal nature of voice vs permanence of text. Words like "Type" or "Click" assume interactions that aren't possible on a phone call. Special characters are awkward when spoken.
You can also use SSML in the responses to allow for more customisation in the audio responses from the assistant.
responses:
utter_contact_support:
- text: "Call our support team at 1-800-555-0123"
- text: |
<speak>
You can reach our support team at
<say-as interpret-as="telephone">1 800 555 0123</say-as>
<break time="500ms"/>
Our agents are available 24/7.
</speak>
channel: "twilio_media_streams"
Reference: Default Pattern Configuration
For reference, here is the complete default configuration for conversation repair:
version: "3.1"
responses:
utter_ask_rephrase:
- text: I’m sorry I am unable to understand you, could you please rephrase?
utter_ask_still_there:
- text: "Hello, are you still there?"
metadata:
rephrase: True
utter_boolean_slot_rejection:
- text: "Sorry, the value you provided, `{{value}}`, is not valid. Please respond with a valid value."
metadata:
rephrase: True
template: jinja
utter_can_do_something_else:
- text: "What else can I help you with?"
metadata:
rephrase: True
utter_cannot_handle:
- text: I'm sorry, I'm not trained to help with that.
utter_categorical_slot_rejection:
- text: "Sorry, you responded with an invalid value - `{{value}}`. Please select one of the available options."
metadata:
rephrase: True
template: jinja
utter_clarification_options_rasa:
- text: "I can help, but I need more information. Which of these would you like to do: {{context.clarification_options}}?"
metadata:
rephrase: True
template: jinja
utter_corrected_previous_input:
- text: "Ok, I am updating {{ context.corrected_slots.keys()|join(', ') }} to {{ context.corrected_slots.values()|join(', ') }} respectively."
metadata:
rephrase: True
template: jinja
utter_float_slot_rejection:
- text: "Sorry, it seems the value you provided `{{value}}` is not a valid number. Please provide a valid number in your response."
metadata:
rephrase: True
template: jinja
utter_flow_cancelled_rasa:
- text: "Okay, stopping {{ context.canceled_name }}."
metadata:
rephrase: True
template: jinja
utter_flow_continue_interrupted:
- text: "Let's continue with {{ context.previous_flow_name }}."
metadata:
rephrase: True
template: jinja
utter_free_chitchat_response:
- text: "Sorry, I'm not able to answer that right now."
metadata:
rephrase: True
rephrase_prompt: |
You are an incredibly friendly assistant. Generate a short
response to the user's comment in simple english.
User: {{current_input}}
Response:
utter_human_handoff_not_available:
- text: I understand you want to be connected to a human agent, but that's something I cannot help you with at the moment. Is there something else I can help you with?
metadata:
rephrase: True
utter_inform_code_change:
- text: There has been an update to my code. I need to wrap up our running dialogue and start from scratch.
metadata:
rephrase: True
utter_inform_hangup:
- text: It seems you are not there anymore. I will hang up shortly.
metadata:
rephrase: True
utter_internal_error_rasa:
- text: Sorry, I am having trouble with that. Please try again in a few minutes.
utter_no_knowledge_base:
- text: I am afraid, I don't know the answer. At this point, I don't have access to a knowledge base.
metadata:
rephrase: True
utter_skip_question_answer:
- text: I'm here to provide you with the best assistance, and in order to do so, I kindly request that we complete this step together. Your input is essential for a seamless experience!
metadata:
rephrase: True
utter_user_input_empty_error_rasa:
- text: I see an empty message. What can I assist you with?
utter_user_input_too_long_error_rasa:
- text: I'm sorry, but your message is too long for me to process. Please keep your message concise and within {% if context.info.max_characters %}{{context.info.max_characters}} characters.{% else %}a reasonable length.{% endif %}
metadata:
template: jinja
slots:
confirm_correction:
type: bool
mappings:
- type: from_llm
silence_timeout:
type: float
initial_value: 6.0
max_value: 1000000
consecutive_silence_timeouts:
type: float
initial_value: 0.0
max_value: 1000000
flows:
pattern_cancel_flow:
description: Conversation repair flow that starts when a flow is cancelled
name: pattern_cancel_flow
steps:
- action: action_cancel_flow
- action: utter_flow_cancelled_rasa
pattern_cannot_handle:
description: |
Conversation repair flow for addressing failed command generation scenarios
name: pattern cannot handle
steps:
- noop: true
next:
# chitchat fallback
- if: "'{{context.reason}}' = 'cannot_handle_chitchat'"
then:
- action: utter_cannot_handle
next: "END"
# fallback for things that are not supported
- if: "'{{context.reason}}' = 'cannot_handle_not_supported'"
then:
- action: utter_cannot_handle
next: END
# default
- else:
- action: utter_ask_rephrase
next: END
pattern_chitchat:
description: Conversation repair flow for off-topic interactions that won't disrupt the main conversation
name: pattern chitchat
steps:
- action: action_trigger_chitchat
pattern_clarification:
description: Conversation repair flow for handling ambiguous requests that could match multiple flows
name: pattern clarification
steps:
- action: action_clarify_flows
- action: utter_clarification_options_rasa
pattern_code_change:
description: Conversation repair flow for cleaning the stack after an assistant update
name: pattern code change
steps:
- action: utter_inform_code_change
- action: action_clean_stack
pattern_collect_information:
description: Flow for collecting information from users
name: pattern collect information
steps:
- id: start
action: action_run_slot_rejections
- action: validate_{{context.collect}}
next:
- if: "slots.{{context.collect}} is not null"
then: END
- else: ask_collect
- id: ask_collect
action: "{{context.utter}}"
- action: "{{context.collect_action}}"
- action: action_listen
next: start
pattern_completed:
description: Flow that asks if the user needs more help after completing their initiated use cases
name: pattern completed
steps:
- action: utter_can_do_something_else
pattern_continue_interrupted:
description: Conversation repair flow for managing when users switch between different flows
name: pattern continue interrupted
steps:
- action: utter_flow_continue_interrupted
pattern_correction:
description: Conversation repair flow for managing user input changes or error corrections
name: pattern correction
steps:
- action: action_correct_flow_slot
next:
- if: not context.is_reset_only
then:
- action: utter_corrected_previous_input
next: END
- else: END
pattern_human_handoff:
description: Conversation repair flow for switching users to a human agent if their request can't be handled
name: pattern human handoff
steps:
- action: utter_human_handoff_not_available
pattern_internal_error:
description: Conversation repair flow for informing users about internal errors
name: pattern internal error
steps:
- noop: true
next:
- if: "'{{context.error_type}}' = 'rasa_internal_error_user_input_too_long'"
then:
- action: utter_user_input_too_long_error_rasa
next: END
- if: "'{{context.error_type}}' = 'rasa_internal_error_user_input_empty'"
then:
- action: utter_user_input_empty_error_rasa
next: END
- else:
- action: utter_internal_error_rasa
next: END
pattern_repeat_bot_messages:
description: Voice conversation repair pattern to repeat bot messages
name: pattern repeat bot messages
steps:
- action: action_repeat_bot_messages
pattern_restart:
description: Flow for restarting the conversation
name: pattern restart
nlu_trigger:
- intent: restart
steps:
- action: action_restart
pattern_search:
description: Flow for handling knowledge-based questions
name: pattern search
steps:
- action: utter_no_knowledge_base
# - action: action_trigger_search to use enterprise search policy if present
pattern_session_start:
description: Flow for starting the conversation
name: pattern session start
nlu_trigger:
- intent: session_start
steps:
- action: action_session_start
pattern_skip_question:
description: Conversation repair flow for managing user intents to skip questions (steps)
name: pattern skip question
steps:
- action: utter_skip_question_answer
pattern_user_silence:
description: Reacting to user silence in voice bots
name: pattern react to silence
nlu_trigger:
- intent: silence_timeout
persisted_slots:
- consecutive_silence_timeouts
steps:
- noop: true
next:
- if: "slots.consecutive_silence_timeouts = 0.0"
then:
- set_slots:
- consecutive_silence_timeouts: 1.0
- action: action_repeat_bot_messages
next: END
- if: "slots.consecutive_silence_timeouts = 1.0"
then:
- set_slots:
- consecutive_silence_timeouts: 2.0
- action: utter_ask_still_there
next: END
- if: "slots.consecutive_silence_timeouts > 1.0"
then:
- action: utter_inform_hangup
- action: action_hangup
next: END
- else: END