Flow Steps
A flow is made up of one or more steps, each with a specific purpose. There are two supported types of flow steps:
- Prescriptive steps: Designed for well-defined, predictable business logic.
- Autonomous steps: Allow for dynamic decision-making and flexible business logic at runtime by triggering a sub agent.
Flows can freely combine both types of steps as needed.
Shared Properties
All steps share these common properties:
- id: "an_optional_unique_id"
next: "an id or conditions to determine which step should be executed next"
Id Property
The id
field is an optional unique identifier for each step. It is used to
reference the step in the next
field of other steps.
Next Property
The next
field specifies which step is executed after this one.
If it is omitted, the following step in the list is the next one.
To link a specific step, you can use the next step's id
:
- collect: name
next: the_next_step
- id: the_next_step
collect: age
You can also create a nested structure by listing steps under the next
field:
- collect: name
next:
- collect: age
This is sometimes easier to read, especially when using conditions to create branching logic. For example:
- collect: age
next:
- if: slots.age < 18
then:
- action: utter_not_old_enough
next: END
- if: slots.age >= 18 and slots.age < 65
then: 18_to_65_step
- else: over_65_step
Depending on the user's input value for age, this example show the flow proceeding to different steps.
Note the age
slot must be prefixed by the slots.
namespace to be accessible in the condition.
For namespaces, navigate to the Namespaces section.
When a condition is met that leads to next: END
, the flow will complete without executing further steps.
Prescriptive Steps
Every step in a flow has a type. The type determines what the step does and what properties it supports. A prescriptive step must have exactly one of they following keys:
action
collect
link
call
set_slots
noop
Action
A step with the key action: action_do_something
instructs your assistant to execute action_do_something
and then proceed to the next step. It will not wait for user input.
The value of the action
key is either the name of a custom action:
- action: action_check_sufficient_funds
Or the name of a response defined in your domain:
- action: utter_insufficient_funds
Collect
A step with a collect
key instructs your assistant to request
information from the user to fill a slot. For example:
- collect: account_type # slot name
Your assistant will not proceed to the next step in the flow until the slot
account_type
has been filled.
See the tutorial for more information
about slot filling and defining slots in your domain file.
Instead of a response
you can also define a custom action called action_ask_recipient
to
phrase the question along with, for example, some buttons to the user.
Make sure to add the custom action to your domain file.
actions:
- action_ask_recipient
You can define either a response
or a custom action
for your collect step.
It is not possible to define both.
A validation error will be thrown by Rasa if both are defined.
Slot Descriptions
An optional description
field can be added to the collect
step to guide the
language model in extracting slot values.
- collect: account_type
description: "Account type is one of checking, savings, money market, or IRA."
The description field can be used to perform slot validation:
- collect: zip_code
description: |
A US postal code, having either 5 or 9 digits.
For example, "60613" or "60613-1060".
Always Asking Questions
By default, a collect
step is skipped if the corresponding slot is already filled.
If a collect
step should always be asked, no matter if the underlying slot
is already filled, you can set ask_before_filling: true
on the collect
step:
- collect: final_confirmation # slot name
ask_before_filling: true
If the "final_confirmation" slot is already filled, the assistant will clear it and proceed to ask the collect
step,
then fill the slot again. This ensures the confirmation collect
step is always asked.
Resetting slots at the End of a Flow
By default, all slots filled via collect
steps are reset when a flow completes.
Once the flow ends, the slot's value is either reset to null
or to the
slot's initial value (if provided in the domain file under slots
).
If you want to retain the value of a slot after the flow completes,
set the reset_after_flow_ends
property to false
. For example:
- collect: user_name
reset_after_flow_ends: false
Configuring reset_after_flow_ends
in collect steps is deprecated in version 3.11.0
and will be removed
in Rasa Pro 4.0.0
. Please use the persisted_slots
property at the flow level instead.
In coexistence mode, this behavior has to be enforced by
annotating the slot definition with the property shared_for_coexistence: True
.
Suppressing interruptions
You can suppress interruptions to a collect
step by setting the force_slot_filling
property to true
.
By default, the assistant will process an interruption to the conversation at any time, this could be triggered by the user digressing or by the assistant incorrectly interpreting the user's message.
In some cases, you may want to suppress interruptions to a collect
step. For example, when the slot being collected is
a feedback or comments slot, and you want to ensure that the user provides their feedback before proceeding with the next step.
This kind of user message could reference different topics which could incorrectly trigger other commands.
By setting force_slot_filling: true
, the assistant will ignore any other commands and only process the SetSlot
command for the slot being collected.
- collect: feedback
force_slot_filling: true
Using a different response key for the collect
step
By default, Rasa will look for a response called utter_ask_{slot_name}
to execute a collect
step.
You can use a response with a different key by adding an utter
property to the step.
For example:
flows:
replace_supplementary_card:
description: This flow helps the user to replace a supplementary card for a family member.
steps:
- collect: account_type
utter: utter_ask_secondary_account_type
next: ask_account_number
Using an action to ask for information in collect
step
Instead of using a templated response for the collect
step you can also use a custom action.
This is useful if you, for example, want to display available values for the slot fetched from a database as buttons.
Rasa will look for an action called action_ask_{slot_name}
to execute a collect
step.
The custom action needs to strictly follow the specified naming convention.
Make sure to add the custom action action_ask_{slot_name}
to your domain file.
actions:
- action_ask_{slot_name}
You don't need to update the collect step itself to use the custom action. The collect step inside the flow stays the same, for example,
- collect: {slot_name}
You can define either a response
or a custom action
for your collect step.
It is not allowed to define both.
A validation error will be thrown by Rasa if both are defined.
Slot validation
You can define slot validation rules directly in your flow yaml file by adding a rejections
property to any
collect
step. The rejections
section is a list of mappings. Each mapping must have
if
and utter
mandatory properties:
- the
if
property is a condition written in natural language and evaluated using the pypred library. In the condition, you can only use the slot name that is collected in this step. At the moment, using other slots is not supported. - the
utter
property is the name of the response the assistant will send if the condition evaluates toTrue
.
Here is an example:
flows:
verify_eligibility:
description: This flow verifies if the user is eligible for creating an account.
steps:
- collect: age
rejections:
- if: slots.age < 1
utter: utter_invalid_age
- if: slots.age < 18
utter: utter_age_at_least_18
next: ask_email
When validation fails, your assistant will automatically try to collect the information again. The assistant will repeatedly ask for the slot until the value is not rejected.
If the predicate defined in the if
property is invalid, the assistant will log an error and respond with the
utter_internal_error_rasa
response which by default is Sorry, I'm having trouble understanding you right now.
Please try again later.
You can override the text message for utter_internal_error_rasa
by adding this response with
the custom text in your domain file.
For more complex validation logic, you can also define slot validation in a custom action.
Note this custom action must follow this naming convention: validate_{slot_name}
.
Link
A link
step is used to connect flows. Links can only be used as the last step in a flow. When a link
step is reached,
the current flow is ended and the targeted flow is started. You can define a link
step as:
- link: "id_of_another_flow"
It is not possible to add any other property like an action
or a next
to a link
step. Doing so, will result in an error during flow validation.
It is possible to link from a flow to the Human Handoff conversation repair pattern. This enables catching a scenario where the conversation should be handed over to a human agent. It is not possible to link from a flow to any of the other conversation repair pattern
Call
The call
step is available starting with version 3.8.0
.
Calling a Flow
You can use a call
step inside a flow (parent flow) to embed another flow (child flow). When the execution reaches a
call
step, CALM starts the child flow. Once the child flow is complete,
the execution continues with the parent flow.
Calling other flows helps split up and reuse functionality. It allows you to define a flow once and use it in multiple other flows. Long flows can be split into smaller ones, making them easier to understand and maintain.
You can define a call
step as:
flows:
parent_flow:
description: "The parent flow calling a child flow."
steps:
- call: child_flow
child_flow:
description: "A child flow."
steps:
- action: utter_child_flow
The call step must reference an existing flow and the child flow can be defined in the same file as the one containing the parent flow or a different file.
Behavior of the call
step
The call
step is designed to behave as if the child flow is part of
the parent flow. Hence, the following properties come out of the box:
- If the child flow has a
next: END
in any step, the control will get passed back to the parent flow, and the next logical step will be executed. - Slots of the child flow can be filled upfront, even before the child
flow is started. The
collect
steps in a child flow behave as if they were directly part of the parent flow. - Slots of the child flow can be corrected once they are filled and the control is still inside the child or the parent flow. So, even after the child flow is complete, the slots can be corrected as long as the parent flow is still active.
- Slots of the parent flow will not be reset when the child flow is activated. The child flow can access and modify slots of the parent flow.
- Slots of a child flow will not be reset when the control comes back to
the parent flow. Instead, they will be reset together with the slots of the
parent flow once that ends (unless
reset_after_flow_ends
is set toFalse
for any of the slots). - If the
CancelFlow()
command is triggered inside the the child flow, the parent flow is also cancelled.
To prevent the child flow from getting triggered directly by a user message, you can add a flow guard to the child flow:
flows:
parent_flow:
description: "A parent flow calling a child flow."
steps:
- call: child_flow
child_flow:
description: "A child flow with a flow guard."
if: False
steps:
- action: utter_child_flow
In the above example, the child_flow
will never be triggered directly by a user message but only when the parent flow calls it.
Constraints on the call
step
Calling other flows has the following constraints by design:
- A child flow can not use the
link
step because alink
step is meant to always terminate the previous flow which would contradict with the behaviour ofcall
step where the control should always be passed to the parent flow. - Patterns can not use the call step.
link
v/s call
If the use case demands a flow to be initiated as a follow-up to another flow,
then a link
step is better suited to accomplish the connection between
the two flows. However, if a flow needs to behave as if it was part of
another larger flow and more steps need to be executed inside the larger flow
after the child flow has completed, then call
step should be used.
Calling an MCP Tool
This feature is currently in beta and is available starting from Rasa 3.14.0.
A call
step can also be used to invoke a tool from an MCP (Model Context Protocol) server directly within a flow.
This allows you to integrate external tools without writing custom actions.
- call: tool_name
mcp_server: server_name
mapping:
input:
- param: parameter_name
slot: slot_name
output:
- slot: result_slot
value: result.structuredContent.value
The MCP server must be configured in your endpoints.yml
file.
See MCP Server Integration for setup details.
When using the call
step with mcp_server
, you must ensure that:
- The
mcp_server
value exactly matches the name of an MCP server defined in yourendpoints.yml
. - The
call
value (tool name) refers to a tool that is available on that MCP server.
If mcp_server
refers to a name not defined in your endpoints.yml
, or if the tool does not exist on that server, the tool call will fail at runtime.
Mapping Configuration
The mapping
section defines how to pass data to the tool and how to store the results:
Input Mapping
The input
section maps slot values to tool parameters. Each input mapping requires:
param
: The parameter name expected by the toolslot
: The slot name containing the value to send
Here's a concrete example of input mapping for a flight booking tool:
mapping:
input:
- param: flight_id
slot: flight_number
- param: num_passengers
slot: passenger_count
Output Mapping
The output
section maps tool results to slots. Each output mapping requires:
slot
: The slot name to store the resultvalue
: A Jinja2 expression that extracts the desired value from the tool result
Here's a concrete example of output mapping for the same flight booking tool:
mapping:
output:
- slot: booking_status
value: result.structuredContent.booking_status.success
- slot: booking_reference
value: result.structuredContent.booking_status.reference_number
The value
field supports Jinja2 expressions, allowing you to:
- Access nested object properties using dot notation
- Apply filters and transformations
- Use conditional logic
Tool Result Formats
MCP tools return results in two possible formats:
Structured Content
When tools provide an output schema, you get structured data as output:
{
"result": {
"structuredContent": {
"booking_status": {"success": true, "reference_number": "ABC123"}
}
}
}
Access specific values by writing a valid Jinja2 expression (often using dot notation). For example:
output:
- slot: booking_status
value: result.structuredContent.booking_status.success
- slot: booking_reference
value: result.structuredContent.booking_status.reference_number
Unstructured Content
When no output schema is defined, the entire output is captured as a serialized string:
{
"result": {
"content": [
{
"type": "text",
"text": "{\"booking_status\": {\"success\": true, \"reference_number\": \"ABC123\"}}"
}
]
}
}
For unstructured content, capture the complete result in a slot and process the content later if needed:
output:
- slot: booking_data
value: result.content
Set Slots
A set_slots
step is used to set one or more slots. For example:
- set_slots:
- account_type: "savings"
- account_number: "123456789"
Normally, slots are either set from user input (using a collect
step) or
by fetching information from another system (using a custom action).
A set_slot
is mostly useful to unset a slot value.
For example, if a user asks to transfer more money than they have available in their account,
you might want to inform them. Just reset the amount
slot rather than ending the flow.
flows:
transfer_money:
description: This flow lets users send money to friends and family.
steps:
- collect: recipient
- id: ask_amount
collect: amount
description: the number of US dollars to send
- action: action_check_sufficient_funds
next:
- if: not has_sufficient_funds
then:
- action: utter_insufficient_funds
- set_slots:
- amount: null
next: ask_amount
- else: final_confirmation
Noop
A noop
step can be combined with the next
property to create a
conditional branch in a flow without necessarily
running an action, uttering a response, collecting a slot or linking / calling another flow in that step itself.
For example:
flows:
change_address:
description: Allow a user to change their address.
steps:
- noop: true
next:
- if: not slots.authenticated
then:
- call: authenticate_user
next: ask_new_address
- else: ask_new_address
- id: ask_new_address
collect: address
...
It's always necessary to add the next
property to a noop
step. Otherwise, validation of flows would fail during training.
Autonomous Steps
Autonomous Steps are currently in beta and are available starting from Rasa 3.14.0.
Autonomous steps allow for dynamic decision-making and flexible business logic at runtime by triggering a sub agent. Unlike prescriptive steps that follow a predetermined path, autonomous steps delegate control to an AI sub agent that can make decisions, use tools, and interact with users independently.
A call
step can invoke an autonomous sub agent that runs until completion or until specific exit conditions are met.
Calling a Sub Agent
- call: agent_name
The agent name specified in the call
step must match a name that has been configured as described in the agent configuration.
It must also be unique and must not clash with any flow name.
The sub agent runs autonomously. It can:
- Invoke different tools based on previous results
- Generate messages to request more information from users
- Signal completion when done
How a sub agent signals completion depends on the sub agent used. For more details please refer to the sub agent documentation.
Specifying Exit Conditions
As an alternative to agent-controlled completion, you can specify explicit conditions that automatically end the sub agent as soon as they are met:
flows:
appointment_booking:
description: helps users book appointments
steps:
- call: booking_agent
exit_if:
- slots.appointment_time is not null
- collect: final_confirmation
This way of specifying exit conditions is only available for the ReAct Sub Agent.
The sub agent automatically gets built-in set_slot
tools for each slot mentioned in the exit conditions.
This allows it to store values that will be used to evaluate when the task is complete.
For more details please refer to the ReAct Sub Agent page.