Version Migration Guide
This page contains information about changes between major versions and how you can migrate from one version to another.
Rasa Pro 3.13 to Rasa Pro 3.14
Dependencies
To avoid any conflicts we strongly recommend using a fresh environment when installing Rasa >=3.14.0.
The default pip package for rasa-pro now supports Python versions 3.12 and 3.13, and drops support for Python version 3.9.
The package will exclude the following dependency categories:
-
nlu- All dependencies required to run NLU/coexistence bots, including:transformers,tensorflow(and related packages:tensorflow-text,tensorflow-hub,tensorflow-gcs-filesystem,tensorflow-metal,tf-keras),spacy,sentencepiece,skops,mitie,jieba,sklearn-crfsuite. -
channels- All dependencies required to connect to channel connectors, including:fbmessenger,twilio,webexteamssdk,mattermostwrapper,rocketchat_API,aiogram,slack-sdk,cvg-python-sdk. Note: The following channels are NOT included in the channels extra:browser_audio,studio_chat,socketIO, andrest(used by inspector for text and voice).
Optional dependency categories are available to install the relevant packages.
Use pip install rasa-pro[nlu] if you have an agent with NLU components.
Similarly use pip install rasa-pro[channels] if you have an agent making use of channel connectors.
Docker images will continue to have the same dependencies as previously, except for the additional packages mcp and a2a-sdk which now form part of the core dependencies.
Important: tensorflow and its related dependencies are only supported for "python_version < '3.12'", so components requiring TensorFlow are not available for Python ≥ 3.12. These components are: DIETClassifier, TEDPolicy, UnexpecTEDIntentPolicy, ResponseSelector, ConveRTFeaturizer, and LanguageModelFeaturizer.
Pattern Continue Interrupted
We modified the pattern_continue_interrupted to ask for confirmation before returning to an interrupted flow.
Here is an example conversation comparing the old and new implementations:
- Old
- New
user: I want to transfer money
bot: how much do you want to transfer?
user: wait what's my balance?
bot: you have 4200 in your account
bot: how much do you want to transfer? # immediately returns to the interrupted flow
user: I want to transfer money
bot: how much do you want to transfer?
user: wait what's my balance?
bot: you have 4200 in your account
bot: Would you like to go back to transferring money? # asks for confirmation before continuing
Rationale for this change:
- Improved UX: Immediately returning to interrupted flows often creates an unnatural conversational experience.
- Error Correction: Sometimes the command generator incorrectly identifies a cancellation + start flow as a digression. This change allows users to guide the assistant in correcting these mistakes.
- Agent Integration: Sub agents sometimes don't have a reliable way to signal completion. When these agents are wrapped in flows and a digression occurs, we need user input to determine if the agent's task is complete.
Here is the comparison between the old and the new pattern_continue_interrupted.
- Old
- New
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_continue_interrupted:
description: Conversation repair flow for managing when users switch between different flows
name: pattern continue interrupted
steps:
- noop: true
next:
- if: context.multiple_flows_interrupted
then: collect_interrupted_flow_to_continue
- else: collect_continue_interrupted_flow_confirmation
- id: collect_interrupted_flow_to_continue
collect: interrupted_flow_to_continue
description: "Fill this slot with the name of the flow the user wants to continue. If the user does not want to continue any of the interrupted flows, fill this slot with 'none'."
next:
- if: slots.interrupted_flow_to_continue is not "none"
then:
- action: action_continue_interrupted_flow
next: END
- else:
- action: action_cancel_interrupted_flows
next: END
- id: collect_continue_interrupted_flow_confirmation
collect: continue_interrupted_flow_confirmation
description: "If the user wants to continue the interrupted flow, fill this slot with true. If the user does not want to continue the interrupted flow, fill this slot with false."
next:
- if: slots.continue_interrupted_flow_confirmation
then:
- action: action_continue_interrupted_flow
next: END
- else:
- action: action_cancel_interrupted_flows
next: END
Command Generator
Prompt Template
No Action Required: If you don't use sub agents, your prompt templates remain unchanged.
Migration Required: When sub agents are used, Rasa automatically switches to new default prompts that include agent support. If you have customized prompt templates for the CompactLLMCommandGenerator or SearchReadyLLMCommandGenerator, you must update your custom prompts to include the new agent-related commands and functionality.
Prompt Templates of the CompactLLMCommandGenerator
- GPT-4o with Agent Support
- Claude 3.5 Sonnet with Agent Support
The prompt template for the gpt-4o-2024-11-20 model with agent support is as follows:
## Task Description
Your task is to analyze the current conversation context and generate a list of actions to start new business processes that we call flows, to extract slots, or respond to small talk and knowledge requests.
---
## Available Flows and Slots
Use the following structured data:
```json
{"flows":[{% for flow in available_flows %}{"name":"{{ flow.name }}","description":{{ flow.description | to_json_escaped_string }}{% if flow.agent_info %},"sub-agents":[{% for agent in flow.agent_info %}{"name":"{{ agent.name }}","description":{{ agent.description | to_json_escaped_string }}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}{% if flow.slots %},"slots":[{% for slot in flow.slots %}{"name":"{{ slot.name }}"{% if slot.description %},"description":{{ slot.description | to_json_escaped_string }}{% endif %}{% if slot.allowed_values %},"allowed_values":{{ slot.allowed_values }}{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]}
```
---
## Available Actions:
* `start flow flow_name`: Starting a flow. For example, `start flow transfer_money` or `start flow list_contacts`.
* `set slot slot_name slot_value`: Slot setting. For example, `set slot transfer_money_recipient Freddy`. Can be used to correct and change previously set values. ONLY use slots that are explicitly defined in the flow's slot list.
* `cancel flow`: Cancelling the current flow.
* `disambiguate flows flow_name1 flow_name2 ... flow_name_n`: Disambiguate which flow should be started when user input is ambiguous by listing the potential flows as options. For example, `disambiguate flows list_contacts add_contact remove_contact ...` if the user just wrote "contacts".
* `provide info`: Responding to the user's questions by supplying relevant information, such as answering FAQs or explaining services.
* `offtopic reply`: Responding to casual or social user messages that are unrelated to any flows, engaging in friendly conversation and addressing off-topic remarks.
* `hand over`: Handing over to a human, in case the user seems frustrated or explicitly asks to speak to one.
* `repeat message`: Repeating the last bot message.{% if active_agent %}
* `continue agent`: Continue the currently active agent {{ active_agent.name }}. This has HIGHEST PRIORITY when an agent is active and the user is responding to agent questions.{% endif %}{% if completed_agents %}
* `restart agent agent_name`: Restart the agent with the given name, in case the user wants to change some answer to a previous question asked by the agent. For example, `restart agent car_research_agent` if the user changed his mind about the car he wants to buy. ONLY use agents that are listed in the `completed_agents` section.{% endif %}
---
## General Tips
* Do not fill slots with abstract values or placeholders.
* For categorical slots try to match the user message with allowed slot values. Use "other" if you cannot match it.
* Set the boolean slots based on the user response. Map positive responses to `True`, and negative to `False`.
* Extract text slot values exactly as provided by the user. Avoid assumptions, format changes, or partial extractions.
* ONLY use `set slot` with slots that are explicitly defined in the current flow's slot list. Do NOT create or assume slots that don't exist.
* Only use information provided by the user.
* Use clarification in ambiguous cases.
* Multiple flows can be started. If a user wants to digress into a second flow, you do not need to cancel the current flow.
* Do not cancel the flow unless the user explicitly requests it.
* Strictly adhere to the provided action format.
* ONLY use the exact actions listed above. Do NOT invent new actions like "respond <message>" or any other variations.
* Focus on the last message and take it one step at a time.
* Use the previous conversation steps only to aid understanding.{% if active_agent %}
* When an agent is active, ALWAYS prioritize `continue agent` over `provide info` or `offtopic reply` unless the user is clearly asking something unrelated to the agent's task.{% endif %}{% if completed_agents %}
* ONLY use `restart agent` with agents that are listed in the `completed_agents` section. Do NOT restart non-existent agents.{% endif %}{% if active_agent or completed_agents %}
* If you're unsure about agent names, refer to the structured data provided in the `Current State` section.{% endif %}
---
## Current State
{% if current_flow != None %}
Use the following structured data:
```json
{"active_flow":{"name":"{{ current_flow }}","current_step":{"requested_slot":"{{ current_slot }}","requested_slot_description":{{ current_slot_description | to_json_escaped_string }}},"slots":[{% for slot in flow_slots %}{"name":"{{ slot.name }}","value":"{{ slot.value }}","type":"{{ slot.type }}"{% if slot.description %},"description":{{ slot.description | to_json_escaped_string }}{% endif %}{% if slot.allowed_values %},"allowed_values":"{{ slot.allowed_values }}"{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]}{% if active_agent %},"active_agent":{"name":"{{ active_agent.name }}","description":{{ active_agent.description | to_json_escaped_string }}}{% endif %}{% if completed_agents %},"completed_agents":[{% for agent in completed_agents %}{"name":"{{ agent.name }}","description":{{ agent.description | to_json_escaped_string }}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}}
```{% else %}
You are currently not inside any flow.{% endif %}
---
## Conversation History
{{ current_conversation }}
---
## Task
Create an action list with one action per line in response to the user's last message: """{{ user_message }}""".
Your action list:
The prompt template for the claude-3-5-sonnet-20240620 / anthropic.claude-3-5-sonnet-20240620-v1:0 model with agent support is as follows:
## Task Description
Your task is to analyze the current conversation context and generate a list of actions to start new business processes that we call flows, to extract slots, or respond to small talk and knowledge requests.
--
## Available Actions:
* `start flow flow_name`: Starting a flow. For example, `start flow transfer_money` or `start flow list_contacts`.
* `set slot slot_name slot_value`: Slot setting. For example, `set slot transfer_money_recipient Freddy`. Can be used to correct and change previously set values. ONLY use slots that are explicitly defined in the flow's slot list.
* `cancel flow`: Cancelling the current flow.
* `disambiguate flows flow_name1 flow_name2 ... flow_name_n`: Disambiguate which flow should be started when user input is ambiguous by listing the potential flows as options. For example, `disambiguate flows list_contacts add_contact remove_contact ...` if the user just wrote "contacts".
* `provide info`: Responding to the user's questions by supplying relevant information, such as answering FAQs or explaining services.
* `offtopic reply`: Responding to casual or social user messages that are unrelated to any flows, engaging in friendly conversation and addressing off-topic remarks.
* `hand over`: Handing over to a human, in case the user seems frustrated or explicitly asks to speak to one.
* `repeat message`: Repeating the last bot message.{% if active_agent %}
* `continue agent`: Continue the currently active agent {{ active_agent.name }}. This has HIGHEST PRIORITY when an agent is active and the user is responding to agent questions.{% endif %}{% if completed_agents %}
* `restart agent agent_name`: Restart the agent with the given name, in case the user wants to change some answer to a previous question asked by the agent. For example, `restart agent car_research_agent` if the user changed his mind about the car he wants to buy. ONLY use agents that are listed in the `completed_agents` section.{% endif %}
--
## General Tips
* Do not fill slots with abstract values or placeholders.
* For categorical slots try to match the user message with allowed slot values. Use "other" if you cannot match it.
* Set the boolean slots based on the user response. Map positive responses to `True`, and negative to `False`.
* Always refer to the slot description to determine what information should be extracted and how it should be formatted.
* For text slots, extract values exactly as provided by the user unless the slot description specifies otherwise. Preserve formatting and avoid rewording, truncation, or making assumptions.
* ONLY use `set slot` with slots that are explicitly defined in the current flow's slot list. Do NOT create or assume slots that don't exist.
* Only use information provided by the user.
* Use clarification in ambiguous cases.
* Multiple flows can be started. If a user wants to digress into a second flow, you do not need to cancel the current flow.
* Do not cancel the flow unless the user explicitly requests it.
* Strictly adhere to the provided action format.
* ONLY use the exact actions listed above. Do NOT invent new actions like "respond <message>" or any other variations.
* Focus on the last message and take it one step at a time.
* Use the previous conversation steps only to aid understanding.{% if active_agent %}
* When an agent is active, ALWAYS prioritize `continue agent` over `provide info` or `offtopic reply` unless the user is clearly asking something unrelated to the agent's task.{% endif %}{% if completed_agents %}
* ONLY use `restart agent` with agents that are listed in the `completed_agents` section. Do NOT restart non-existent agents.{% endif %}{% if active_agent or completed_agents %}
* If you're unsure about agent names, refer to the structured data provided in the `Current State` section.{% endif %}
--
## Available Flows and Slots
Use the following structured data:
```json
{"flows":[{% for flow in available_flows %}{"name":"{{ flow.name }}","description":{{ flow.description | to_json_escaped_string }}{% if flow.agent_info %},"sub-agents":[{% for agent in flow.agent_info %}{"name":"{{ agent.name }}","description":{{ agent.description | to_json_escaped_string }}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}{% if flow.slots %},"slots":[{% for slot in flow.slots %}{"name":"{{ slot.name }}"{% if slot.description %},"description":{{ slot.description | to_json_escaped_string }}{% endif %}{% if slot.allowed_values %},"allowed_values":{{ slot.allowed_values }}{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]}
```
--
## Current State
{% if current_flow != None %}Use the following structured data:
```json
{"active_flow":{"name":"{{ current_flow }}","current_step":{"requested_slot":"{{ current_slot }}","requested_slot_description":{{ current_slot_description | to_json_escaped_string }}},"slots":[{% for slot in flow_slots %}{"name":"{{ slot.name }}","value":"{{ slot.value }}","type":"{{ slot.type }}"{% if slot.description %},"description":{{ slot.description | to_json_escaped_string }}{% endif %}{% if slot.allowed_values %},"allowed_values":"{{ slot.allowed_values }}"{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]}{% if active_agent %},"active_agent":{"name":"{{ active_agent.name }}","description":{{ active_agent.description | to_json_escaped_string }}}{% endif %}{% if completed_agents %},"completed_agents":[{% for agent in completed_agents %}{"name":"{{ agent.name }}","description":{{ agent.description | to_json_escaped_string }}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}}
```{% else %}
You are currently not inside any flow.{% endif %}
---
## Conversation History
{{ current_conversation }}
---
## Task
Create an action list with one action per line in response to the user's last message: """{{ user_message }}""".
Your action list:
Prompt Templates of the SearchReadyLLMCommandGenerator
- GPT-4o with Agent Support
- Claude 3.5 Sonnet with Agent Support
The prompt template for the gpt-4o-2024-11-20 model with agent support is as follows:
## Task Description
Your task is to analyze the current conversation context and generate a list of actions to start new business processes that we call flows, to extract slots, or respond to off-topic and knowledge requests.
---
## Available Flows and Slots
Use the following structured data:
```json
{"flows":[{% for flow in available_flows %}{"name":"{{ flow.name }}","description":{{ flow.description | to_json_escaped_string }}{% if flow.agent_info %},"sub-agents":[{% for agent in flow.agent_info %}{"name":"{{ agent.name }}","description":{{ agent.description | to_json_escaped_string }}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}{% if flow.slots %},"slots":[{% for slot in flow.slots %}{"name":"{{ slot.name }}"{% if slot.description %},"description":{{ slot.description | to_json_escaped_string }}{% endif %}{% if slot.allowed_values %},"allowed_values":{{ slot.allowed_values }}{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]}
```
---
## Available Actions:
* `start flow flow_name`: Start a flow. For example, `start flow transfer_money` or `start flow list_contacts`.
* `set slot slot_name slot_value`: Set a slot for the active flow. For example, `set slot transfer_money_recipient Freddy`. Can be used to correct and change previously set values. ONLY use slots that are explicitly defined in the flow's slot list.
* `disambiguate flows flow_name1 flow_name2 ... flow_name_n`: When a message could refer to multiple flows, list the possible flows as options to clarify. Example: `disambiguate flows list_contacts add_contact remove_contact`.
* `search and reply`: Provide a response from the knowledge base to address the user’s inquiry when no flows fit, including domain knowledge, FAQs, and all off-topic or social messages.
* `cancel flow`: Cancel the current flow if the user requests it.
* `repeat message`: Repeat the last bot message.{% if active_agent %}
* `continue agent`: Continue the currently active agent {{ active_agent.name }}. This has HIGHEST PRIORITY when an agent is active and the user is responding to agent questions.{% endif %}{% if completed_agents %}
* `restart agent agent_name`: Restart the agent with the given name, in case the user wants to change some answer to a previous question asked by the agent. For example, `restart agent car_research_agent` if the user changed his mind about the car he wants to buy. ONLY use agents that are listed in the `completed_agents` section.{% endif %}
---
## General Instructions
### Start Flow
* Only start a flow if the user's message is clear and fully addressed by that flow's description and purpose.
* Pay close attention to exact wording and scope in the flow description — do not assume or “stretch” the intended use of a flow.
### Set Slot
* Do not fill slots with abstract values or placeholders.
* For categorical slots try to match the user message with allowed slot values. Use "other" if you cannot match it.
* Set the boolean slots based on the user response. Map positive responses to `True`, and negative to `False`.
* Extract text slot values exactly as provided by the user. Avoid assumptions, format changes, or partial extractions.
* ONLY use `set slot` with slots that are explicitly defined in the current flow's slot list. Do NOT create or assume slots that don't exist.
### Disambiguate Flows
* Use `disambiguate flows` when the user's message matches multiple flows and you cannot decide which flow is most appropriate.
* If the user message is short and not precise enough to start a flow or `search and reply`, disambiguate.
* If a single flow is a strong/plausible fit, prefer starting that flow directly.
* If a user's message unambiguously and distinctly matches multiple flows, start all relevant flows at once (rather than disambiguating).
### Search and Reply
* Only start `search and reply` if the user intent is clear.
* Flow Priority: If you are unsure between starting a flow or `search and reply`, always prioritize starting a flow.
### Cancel Flow
* Do not cancel any flow unless the user explicitly requests it.
* Multiple flows can be started without cancelling the previous, if the user wants to pursue multiple processes.{% if active_agent or completed_agents %}
### Agents{% if active_agent %}
* When an agent is active, ALWAYS prioritize `continue agent` over `search and reply` unless the user is clearly asking something unrelated to the agent's task.{% endif %}{% if completed_agents %}
* ONLY use `restart agent` with agents that are listed in the `completed_agents` section. Do NOT restart non-existent agents.{% endif %}
* If you're unsure about agent names, refer to the structured data provided in the `Current State` section.
{% endif %}### General Tips
* Only use information provided by the user.
* Strictly adhere to the provided action format.
* ONLY use the exact actions listed above. Do NOT invent new actions like "respond <message>" or any other variations.
* Focus on the last message and take it one step at a time.
* Use the previous conversation steps only to aid understanding.
---
## Decision Rule Table
| Condition | Action |
|---------------------------------------------------------------|--------------------|{% if active_agent %}
| Agent is active and the user is responding to agent questions | continue agent |{% endif %}
| Flow perfectly matches user's message | start flow |
| Multiple flows are equally strong, relevant matches | disambiguate flows |
| User's message is unclear or imprecise | disambiguate flows |
| No flow fits at all, but knowledge base may help | search and reply |
---
## Current State
{% if current_flow != None %}Use the following structured data:
```json
{"active_flow":{"name":"{{ current_flow }}","current_step":{"requested_slot":"{{ current_slot }}","requested_slot_description":{{ current_slot_description | to_json_escaped_string }}},"slots":[{% for slot in flow_slots %}{"name":"{{ slot.name }}","value":"{{ slot.value }}","type":"{{ slot.type }}"{% if slot.description %},"description":{{ slot.description | to_json_escaped_string }}{% endif %}{% if slot.allowed_values %},"allowed_values":"{{ slot.allowed_values }}"{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]}{% if active_agent %},"active_agent":{"name":"{{ active_agent.name }}","description":{{ active_agent.description | to_json_escaped_string }}}{% endif %}{% if completed_agents %},"completed_agents":[{% for agent in completed_agents %}{"name":"{{ agent.name }}","description":{{ agent.description | to_json_escaped_string }}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}}
```{% else %}
You are currently not inside any flow.{% endif %}
---
## Conversation History
{{ current_conversation }}
---
## Task
Create an action list with one action per line in response to the user's last message: """{{ user_message }}""".
Your action list:
The prompt template for the claude-3-5-sonnet-20240620 / anthropic.claude-3-5-sonnet-20240620-v1:0 model with agent support is as follows:
## Task Description
Your task is to analyze the current conversation context and generate a list of actions to start new business processes that we call flows, to extract slots, or respond to off-topic and knowledge requests.
---
## Available Actions:
* `start flow flow_name`: Start a flow. For example, `start flow transfer_money` or `start flow list_contacts`.
* `set slot slot_name slot_value`: Set a slot for the active flow. For example, `set slot transfer_money_recipient Freddy`. Can be used to correct and change previously set values. ONLY use slots that are explicitly defined in the flow's slot list.
* `disambiguate flows flow_name1 flow_name2 ... flow_name_n`: When a message could refer to multiple flows, list the possible flows as options to clarify. Example: `disambiguate flows list_contacts add_contact remove_contact`.
* `search and reply`: Provide a response from the knowledge base to address the user's inquiry when no flows fit, including domain knowledge, FAQs, and all off-topic or social messages.
* `cancel flow`: Cancel the current flow if the user requests it.
* `repeat message`: Repeat the last bot message.{% if active_agent %}
* `continue agent`: Continue the currently active agent {{ active_agent.name }}. This has HIGHEST PRIORITY when an agent is active and the user is responding to agent questions.{% endif %}{% if completed_agents %}
* `restart agent agent_name`: Restart the agent with the given name, in case the user wants to change some answer to a previous question asked by the agent. For example, `restart agent car_research_agent` if the user changed his mind about the car he wants to buy. ONLY use agents that are listed in the `completed_agents` section.{% endif %}
---
## General Instructions
### Start Flow
* Only start a flow if the user's message is clear and fully addressed by that flow's description and purpose.
* Pay close attention to exact wording and scope in the flow description — do not assume or “stretch” the intended use of a flow.
### Set Slot
* Do not fill slots with abstract values or placeholders.
* For categorical slots, try to match the user message with allowed slot values. Use "other" if you cannot match it.
* Set the boolean slots based on the user's response. Map positive responses to `True`, and negative to `False`.
* Extract text slot values exactly as provided by the user. Avoid assumptions, format changes, or partial extractions.
* ONLY use `set slot` with slots that are explicitly defined in the current flow's slot list. Do NOT create or assume slots that don't exist.
### Disambiguate Flows
* Use `disambiguate flows` when the user's message matches multiple flows and you cannot decide which flow is most appropriate.
* If the user message is short and not precise enough to start a flow or `search and reply`, disambiguate.
* If a single flow is a strong/plausible fit, prefer starting that flow directly.
* If a user's message unambiguously and distinctly matches multiple flows, start all relevant flows at once (rather than disambiguating).
### Search and Reply
* Only start `search and reply` if the user intent is clear.
* Flow Priority: If you are unsure between starting a flow or `search and reply`, always prioritize starting a flow.
### Cancel Flow
* Do not cancel any flow unless the user explicitly requests it.
* Multiple flows can be started without cancelling the previous, if the user wants to pursue multiple processes.{% if active_agent or completed_agents %}
### Agents{% if active_agent %}
* When an agent is active, ALWAYS prioritize `continue agent` over `search and reply` unless the user is clearly asking something unrelated to the agent's task.{% endif %}{% if completed_agents %}
* ONLY use `restart agent` with agents that are listed in the `completed_agents` section. Do NOT restart non-existent agents.{% endif %}
* If you're unsure about agent names, refer to the structured data provided in the `Current State` section.
{% endif %}### General Tips
* Only use information provided by the user.
* Strictly adhere to the provided action format.
* ONLY use the exact actions listed above. Do NOT invent new actions like "respond <message>" or any other variations.
* Focus on the last message and take it one step at a time.
* Use the previous conversation steps only to aid understanding.
---
## Decision Rule Table
| Condition | Action |
|---------------------------------------------------------------|--------------------|{% if active_agent %}
| Agent is active and the user is responding to agent questions | continue agent |{% endif %}
| Flow perfectly matches user's message | start flow |
| Multiple flows are equally strong, relevant matches | disambiguate flows |
| User's message is unclear or imprecise | disambiguate flows |
| No flow fits at all, but knowledge base may help | search and reply |
---
## Available Flows and Slots
Use the following structured data:
```json
{"flows":[{% for flow in available_flows %}{"name":"{{ flow.name }}","description":{{ flow.description | to_json_escaped_string }}{% if flow.agent_info %},"sub-agents":[{% for agent in flow.agent_info %}{"name":"{{ agent.name }}","description":{{ agent.description | to_json_escaped_string }}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}{% if flow.slots %},"slots":[{% for slot in flow.slots %}{"name":"{{ slot.name }}"{% if slot.description %},"description":{{ slot.description | to_json_escaped_string }}{% endif %}{% if slot.allowed_values %},"allowed_values":{{ slot.allowed_values }}{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]}
```
---
## Current State
{% if current_flow != None %}Use the following structured data:
```json
{"active_flow":{"name":"{{ current_flow }}","current_step":{"requested_slot":"{{ current_slot }}","requested_slot_description":{{ current_slot_description | to_json_escaped_string }}},"slots":[{% for slot in flow_slots %}{"name":"{{ slot.name }}","value":"{{ slot.value }}","type":"{{ slot.type }}"{% if slot.description %},"description":{{ slot.description | to_json_escaped_string }}{% endif %}{% if slot.allowed_values %},"allowed_values":"{{ slot.allowed_values }}"{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]}{% if active_agent %},"active_agent":{"name":"{{ active_agent.name }}","description":{{ active_agent.description | to_json_escaped_string }}}{% endif %}{% if completed_agents %},"completed_agents":[{% for agent in completed_agents %}{"name":"{{ agent.name }}","description":{{ agent.description | to_json_escaped_string }}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}}
```{% else %}
You are currently not inside any flow.{% endif %}
---
## Conversation History
{{ current_conversation }}
---
## Task
Create an action list with one action per line in response to the user's last message: """{{ user_message }}""".
Your action list:
Template Rendering
We updated the render_template method to include sub agent information.
If you customized the rendering method, add the new sub agent information to your implementation. The key changes to the function are highlighted below.
def render_template(
self,
message: Message,
tracker: DialogueStateTracker,
startable_flows: FlowsList,
all_flows: FlowsList,
) -> str:
"""Render the jinja template to create the prompt for the LLM.
Args:
message: The current message from the user.
tracker: The tracker containing the current state of the conversation.
startable_flows: The flows startable at this point in time by the user.
all_flows: all flows present in the assistant
Returns:
The rendered prompt template.
"""
# need to make this distinction here because current step of the
# top_calling_frame would be the call step, but we need the collect step from
# the called frame. If no call is active calling and called frame are the same.
top_calling_frame = top_flow_frame(tracker.stack)
top_called_frame = top_flow_frame(tracker.stack, ignore_call_frames=False)
top_flow = top_calling_frame.flow(all_flows) if top_calling_frame else None
current_step = top_called_frame.step(all_flows) if top_called_frame else None
flow_slots = self.prepare_current_flow_slots_for_template(
top_flow, current_step, tracker
)
current_slot, current_slot_description = self.prepare_current_slot_for_template(
current_step
)
current_slot_type = None
current_slot_allowed_values = None
if current_slot:
current_slot_type = (
slot.type_name
if (slot := tracker.slots.get(current_slot)) is not None
else None
)
current_slot_allowed_values = allowed_values_for_slot(
tracker.slots.get(current_slot)
)
has_agents = Configuration.get_instance().available_agents.has_agents()
current_conversation = tracker_as_readable_transcript(
tracker, highlight_agent_turns=has_agents
)
latest_user_message = sanitize_message_for_prompt(message.get(TEXT))
current_conversation += f"\nUSER: {latest_user_message}"
inputs: Dict[str, Any] = {
"available_flows": self.prepare_flows_for_template(
startable_flows,
tracker,
add_agent_info=has_agents,
),
"current_conversation": current_conversation,
"flow_slots": flow_slots,
"current_flow": top_flow.id if top_flow is not None else None,
"current_slot": current_slot,
"current_slot_description": current_slot_description,
"current_slot_type": current_slot_type,
"current_slot_allowed_values": current_slot_allowed_values,
"user_message": latest_user_message,
}
if has_agents:
inputs["active_agent"] = (
get_active_agent_info(tracker, top_flow.id) if top_flow else None
)
inputs["completed_agents"] = get_completed_agents_info(tracker)
return self.compile_template(self.prompt_template).render(**inputs)
LLM Clients
We updated the signature of the completion and acompletion functions in our LLMClient protocol to support LLM calls with tools:
def completion(
self, messages: Union[List[dict], List[str], str], **kwargs: Any
) -> LLMResponse:
async def acompletion(
self, messages: Union[List[dict], List[str], str], **kwargs: Any
) -> LLMResponse:
The **kwargs are passed through to the underlying LiteLLM completion functions.
Migration Required: If you have modified any existing LLM clients or implemented custom clients, update your completion and acompletion methods to match the new signature.
Here are a code snippets of the updated implementations in _BaseLiteLLMClient:
- Synchronous
- Asynchronous
def completion(
self, messages: Union[List[dict], List[str], str], **kwargs: Any
) -> LLMResponse:
"""Synchronously generate completions for given list of messages.
Args:
messages: The message can be,
- a list of preformatted messages. Each message should be a dictionary
with the following keys:
- content: The message content.
- role: The role of the message (e.g. user or system).
- a list of messages. Each message is a string and will be formatted
as a user message.
- a single message as a string which will be formatted as user message.
**kwargs: Additional parameters to pass to the completion call.
Returns:
List of message completions.
Raises:
ProviderClientAPIException: If the API request fails.
"""
...
response = litellm.completion(
messages=formatted_messages, **{**arguments, **kwargs}
)
...
async def acompletion(
self, messages: Union[List[dict], List[str], str], **kwargs: Any
) -> LLMResponse:
"""Asynchronously generate completions for given list of messages.
Args:
messages: The message can be,
- a list of preformatted messages. Each message should be a dictionary
with the following keys:
- content: The message content.
- role: The role of the message (e.g. user or system).
- a list of messages. Each message is a string and will be formatted
as a user message.
- a single message as a string which will be formatted as user message.
**kwargs: Additional parameters to pass to the completion call.
Returns:
List of message completions.
Raises:
ProviderClientAPIException: If the API request fails.
"""
...
response = await litellm.acompletion(
messages=formatted_messages, **{**arguments, **kwargs}
)
...
Add tool_calls to LLMResponse
We added a tool_calls field to the LLMResponse class to capture tool calls from LLM responses.
The _format_response function was updated to extract tool calls from the LiteLLM response.
Migration Impact: If you have custom code that processes LLMResponse objects, you may need to handle the new tool_calls field.
@dataclass
class LLMResponse:
id: str
"""A unique identifier for the completion."""
choices: List[str]
"""The list of completion choices the model generated for the input prompt."""
created: int
"""The Unix timestamp (in seconds) of when the completion was created."""
model: Optional[str] = None
"""The model used for completion."""
usage: Optional[LLMUsage] = None
"""An optional details about the token usage for the API call."""
additional_info: Optional[Dict] = None
"""Optional dictionary for storing additional information related to the
completion that may not be covered by other fields."""
latency: Optional[float] = None
"""Optional field to store the latency of the LLM API call."""
tool_calls: Optional[List[LLMToolCall]] = None
"""The list of tool calls the model generated for the input prompt."""
class LLMToolCall(BaseModel):
"""A class representing a response from an LLM tool call."""
id: str
"""The ID of the tool call."""
tool_name: str
"""The name of the tool that was called."""
tool_args: Dict[str, Any]
"""The arguments passed to the tool call."""
type: str = "function"
"""The type of the tool call."""
Command
Migration Impact: The changes mentioned below improve conversation flow handling and agent state management. No action required unless you have custom command implementations.
StartFlow Command
We updated the run_command_on_tracker method to handle StartFlow commands when users are in pattern_continue_interrupted state, ensuring smooth conversation flow by cleaning up the pattern.
We also added proper agent state management to handle agent interruptions.
Flow Resumption: Previously, StartFlow commands for flows already on the stack (but not active) were ignored. This behavior was updated to resume the flow instead.
def run_command_on_tracker(
self,
tracker: DialogueStateTracker,
all_flows: FlowsList,
original_tracker: DialogueStateTracker,
) -> List[Event]:
"""Runs the command on the tracker.
Args:
tracker: The tracker to run the command on.
all_flows: All flows in the assistant.
original_tracker: The tracker before any command was executed.
Returns:
The events to apply to the tracker.
"""
stack = tracker.stack
original_stack = original_tracker.stack
applied_events: List[Event] = []
if self.flow not in all_flows.flow_ids:
structlogger.debug(
"start_flow_command.skip_command.start_invalid_flow_id", command=self
)
return []
original_user_frame = top_user_flow_frame(original_stack)
original_top_flow = (
original_user_frame.flow(all_flows) if original_user_frame else None
)
# if the original top flow is the same as the flow to start, the flow is
# already active, do nothing
if original_top_flow is not None and original_top_flow.id == self.flow:
# in case continue_interrupted is not active, skip the already active start
# flow command
if not is_continue_interrupted_flow_active(stack):
return []
# if the continue interrupted flow is active, and the command generator
# predicted a start flow command for the flow which is on top of the stack,
# we just need to remove the pattern_continue_interrupted frame(s) from the
# stack
stack, flow_completed_events = remove_pattern_continue_interrupted_frames(
stack
)
applied_events.extend(flow_completed_events)
return applied_events + tracker.create_stack_updated_events(stack)
# if the flow is already on the stack, resume it
if (
self.flow in user_flows_on_the_stack(stack)
and original_user_frame is not None
):
# if pattern_continue_interrupted is active, we need to remove it
# from the stack before resuming the flow
stack, flow_completed_events = remove_pattern_continue_interrupted_frames(
stack
)
applied_events.extend(flow_completed_events)
applied_events.extend(resume_flow(self.flow, tracker, stack))
# the current active flow is interrupted
applied_events.append(
FlowInterrupted(
original_user_frame.flow_id, original_user_frame.step_id
)
)
return applied_events
frame_type = FlowStackFrameType.REGULAR
# remove the pattern_continue_interrupted frames from the stack
# if it is currently active but the user digressed from the pattern
stack, flow_completed_events = remove_pattern_continue_interrupted_frames(stack)
applied_events.extend(flow_completed_events)
if original_top_flow:
# if the original top flow is not the same as the flow to start,
# interrupt the current active flow
frame_type = FlowStackFrameType.INTERRUPT
if original_user_frame is not None:
applied_events.append(
FlowInterrupted(
original_user_frame.flow_id, original_user_frame.step_id
)
)
# If there is an active agent frame, interrupt it
active_agent_stack_frame = stack.find_active_agent_frame()
if active_agent_stack_frame:
structlogger.debug(
"start_flow_command.interrupt_agent",
command=self,
agent_id=active_agent_stack_frame.agent_id,
frame_id=active_agent_stack_frame.frame_id,
flow_id=active_agent_stack_frame.flow_id,
)
active_agent_stack_frame.state = AgentState.INTERRUPTED
applied_events.append(
AgentInterrupted(
active_agent_stack_frame.agent_id,
active_agent_stack_frame.flow_id,
)
)
structlogger.debug("start_flow_command.start_flow", command=self)
stack.push(UserFlowStackFrame(flow_id=self.flow, frame_type=frame_type))
return applied_events + tracker.create_stack_updated_events(stack)
Cancel Command
We updated the run_command_on_tracker method to properly handle agent cancellation when flows are canceled, ensuring active agent stack frames are removed and agents are properly canceled.
def run_command_on_tracker(
self,
tracker: DialogueStateTracker,
all_flows: FlowsList,
original_tracker: DialogueStateTracker,
) -> List[Event]:
"""Runs the command on the tracker.
Args:
tracker: The tracker to run the command on.
all_flows: All flows in the assistant.
original_tracker: The tracker before any command was executed.
Returns:
The events to apply to the tracker.
"""
stack = tracker.stack
original_stack = original_tracker.stack
applied_events: List[Event] = []
user_frame = top_user_flow_frame(
original_stack, ignore_call_and_link_frames=False
)
current_flow = user_frame.flow(all_flows) if user_frame else None
if not current_flow:
structlogger.debug(
"cancel_command.skip_cancel_flow.no_active_flow", command=self
)
return []
if agent_frame := original_tracker.stack.find_active_agent_stack_frame_for_flow(
current_flow.id
):
structlogger.debug(
"cancel_command.remove_agent_stack_frame",
command=self,
frame=agent_frame,
)
remove_agent_stack_frame(stack, agent_frame.agent_id)
applied_events.append(
AgentCancelled(agent_id=agent_frame.agent_id, flow_id=current_flow.id)
)
# we pass in the original dialogue stack (before any of the currently
# predicted commands were applied) to make sure we don't cancel any
# frames that were added by the currently predicted commands.
canceled_frames = self.select_canceled_frames(original_stack)
stack.push(
CancelPatternFlowStackFrame(
canceled_name=current_flow.readable_name(
language=tracker.current_language
),
canceled_frames=canceled_frames,
)
)
if user_frame:
applied_events.append(FlowCancelled(user_frame.flow_id, user_frame.step_id))
return applied_events + tracker.create_stack_updated_events(stack)
Clarify Command
We updated the run_command_on_tracker method to properly signal agent interruption when clarification is needed.
def run_command_on_tracker(
self,
tracker: DialogueStateTracker,
all_flows: FlowsList,
original_tracker: DialogueStateTracker,
) -> List[Event]:
"""Runs the command on the tracker.
Args:
tracker: The tracker to run the command on.
all_flows: All flows in the assistant.
original_tracker: The tracker before any command was executed.
Returns:
The events to apply to the tracker.
"""
flows = [all_flows.flow_by_id(opt) for opt in self.options]
clean_options = [flow.id for flow in flows if flow is not None]
if len(clean_options) != len(self.options):
structlogger.debug(
"clarify_command.altered_command.dropped_clarification_options",
command=self,
original_options=self.options,
cleaned_options=clean_options,
)
if len(clean_options) == 0:
structlogger.debug(
"clarify_command.skip_command.empty_clarification", command=self
)
return []
stack = tracker.stack
relevant_flows = [all_flows.flow_by_id(opt) for opt in clean_options]
names = [
flow.readable_name(language=tracker.current_language)
for flow in relevant_flows
if flow is not None
]
applied_events: List[Event] = []
# if the top stack frame is an agent stack frame, we need to
# update the state to INTERRUPTED and add an AgentInterrupted event
if top_stack_frame := stack.top():
if isinstance(top_stack_frame, AgentStackFrame):
applied_events.append(
AgentInterrupted(
top_stack_frame.agent_id,
top_stack_frame.flow_id,
)
)
top_stack_frame.state = AgentState.INTERRUPTED
stack.push(ClarifyPatternFlowStackFrame(names=names))
return applied_events + tracker.create_stack_updated_events(stack)
ChitChat Command
We updated the run_command_on_tracker method to properly signal agent interruption when handling chitchat.
def run_command_on_tracker(
self,
tracker: DialogueStateTracker,
all_flows: FlowsList,
original_tracker: DialogueStateTracker,
) -> List[Event]:
"""Runs the command on the tracker.
Args:
tracker: The tracker to run the command on.
all_flows: All flows in the assistant.
original_tracker: The tracker before any command was executed.
Returns:
The events to apply to the tracker.
"""
stack = tracker.stack
applied_events: List[Event] = []
# if the top stack frame is an agent stack frame, we need to
# update the state to INTERRUPTED and add an AgentInterrupted event
if top_stack_frame := stack.top():
if isinstance(top_stack_frame, AgentStackFrame):
applied_events.append(
AgentInterrupted(
top_stack_frame.agent_id,
top_stack_frame.flow_id,
)
)
top_stack_frame.state = AgentState.INTERRUPTED
stack.push(ChitchatPatternFlowStackFrame())
return applied_events + tracker.create_stack_updated_events(stack)
KnowledgeAnswer Command
We updated the run_command_on_tracker method to properly signal agent interruption when handling knowledge requests.
def run_command_on_tracker(
self,
tracker: DialogueStateTracker,
all_flows: FlowsList,
original_tracker: DialogueStateTracker,
) -> List[Event]:
"""Runs the command on the tracker.
Args:
tracker: The tracker to run the command on.
all_flows: All flows in the assistant.
original_tracker: The tracker before any command was executed.
Returns:
The events to apply to the tracker.
"""
stack = tracker.stack
applied_events: List[Event] = []
# if the top stack frame is an agent stack frame, we need to
# update the state to INTERRUPTED and add an AgentInterrupted event
if top_stack_frame := stack.top():
if isinstance(top_stack_frame, AgentStackFrame):
applied_events.append(
AgentInterrupted(
top_stack_frame.agent_id,
top_stack_frame.flow_id,
)
)
top_stack_frame.state = AgentState.INTERRUPTED
stack.push(SearchPatternFlowStackFrame())
return applied_events + tracker.create_stack_updated_events(stack)
Tracing for Jaeger
We updated the port configuration for Jaeger tracing collection.
Migration Required: Update your Jaeger configuration to use the new port settings.
Updated Docker Command:
docker run --rm --name jaeger \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
-p 5778:5778 \
-p 9411:9411 \
cr.jaegertracing.io/jaegertracing/jaeger:2.10.0
Updated Configuration:
Update your endpoints.yml file with the new port configuration:
tracing:
type: jaeger
host: 0.0.0.0
port: 4317
service_name: rasa
sync_export: ~
The Jaeger UI is now accessible at http://localhost:16686/search.