ReAct Sub Agents are currently in beta and are available starting from Rasa 3.14.0.
- General-purpose ReAct Sub Agent: Handles open-ended tasks and requires explicit completion signaling
- Task-specific ReAct Sub Agent: Focuses on filling specific slots with automatic completion based on exit conditions
General-purpose ReAct Sub Agent
A general-purpose ReAct sub agent is designed for open-ended, exploratory tasks where the agent itself determines when the task is complete. Unlike task-specific agents that have predefined exit conditions, general-purpose agents require explicit completion signaling through a built-intask_completed tool.
Key Characteristics
- Open-ended tasks: Handles exploratory conversations and complex, multi-step tasks
- Autonomous completion: The agent decides when it has fully completed its assigned task
- Explicit signaling: Uses the built-in
task_completedtool to signal completion - No exit conditions: Invoked via autonomous call steps without exit conditions
The task_completed Tool
General-purpose ReAct sub agents have access to a built-in task_completed tool that ends the ReAct loop and returns control to the flow.
The tool is registered automatically and always uses the name task_completed so the runtime can recognize completion.
The tool has no parameters.
When calling task_completed, the model should include a short, natural user-facing message in the same response.
This message appears directly to the user and serves as a brief closing line for that turn.
You can change or replace this tool by subclassing MCPOpenAgent and overriding get_task_completed_tool().
Tool definition
task_completed tool schema
get_task_completed_tool() if you need to give different instructions to the model.
How Completion Works
- Agent decision: The ReAct sub agent autonomously determines when its primary task is complete and the execution loop should finish.
- Tool call: When ready to complete, the model calls the
task_completedtool once. The tool has no parameters; the closing line for the user is agent message in the same turn (a short follow-up to the user’s last message—not a recap of the whole conversation, and not a message argument on the tool). - Completion signal: The
task_completedcall terminates the ReAct sub agent’s execution loop. If the model also invoked other MCP tools in that same response, Rasa still processes those tool results before the run completes. The run ends with statuscompleted. - User response: The user sees the streamed agent message from that turn. The tool itself only returns a fixed internal result (
Task completed); that value is not a user-facing parameter. - Control return: Control returns to the main flow, which can then proceed to the next step.
task_completed with no agent message
If the model calls task_completed with no agent message in the same turn, Rasa does not finish the run yet.
Rasa adds a one-turn system message: do not call tools, only send a short closing line to the user.
It calls the LLM again with tools disabled.
The run then completes from that text-only reply, without calling task_completed again.
Task-specific ReAct Sub Agent
A task-specific ReAct sub agent is designed for structured data collection tasks where specific slots need to be filled with values. Unlike general-purpose agents that determine their own completion, task-specific agents automatically complete when predefined exit conditions are met, making them ideal for appointment booking, form filling, or data collection scenarios.Key Characteristics
- Structured data collection: Focuses on filling specific slots with values.
- Automatic completion: Completes when exit conditions are satisfied.
- Built-in slot tools: Automatically gets
set_slot_<slot_name>tools for each slot in exit conditions. - Exit condition driven: Invoked via autonomous call steps with
exit_ifconditions.
The set_slot_<slot_name> Tools
Task-specific ReAct sub agents automatically receive built-in set_slot_<slot_name> tools for each slot mentioned in the exit_if conditions. These tools are dynamically generated based on the slot definitions and cannot be disabled.
Tool Generation
For each slot in the exit conditions, a tool is created with the following pattern:- Tool name:
set_slot_<slot_name>(e.g.,set_slot_user_name,set_slot_appointment_date) - Dynamic description: Includes slot type and allowed values (for categorical slots)
- Slot-specific parameters: Tailored to the slot’s data type and constraints
Example Tool Definition
For a slot nameduser_name of type text:
set_slot_user_name tool schema (example)
category of type categorical with allowed values ["A", "B", "C"]:
set_slot_category tool schema (example, categorical)
How Slot Setting Works
- Tool Availability: The ReAct sub agent automatically receives
set_slot_<slot_name>tools for all slots mentioned in theexit_ifconditions. - Value Assignment: When the ReAct sub agent calls a
set_slot_<slot_name>tool, the slot value is immediately updated. - Condition Evaluation: After each slot update, the system checks if all exit conditions are satisfied.
- Automatic Completion: Once all exit conditions are met, the ReAct sub agent automatically completes without sending a response.
- Control Return: Control returns to the main flow, which can proceed to the next step.
Configuration
ReAct sub agents extend the basic sub agent configuration with additional settings. In addition to the requiredagent section, ReAct sub agents support the following configuration options:
ReAct sub agent config.yml (illustrative)
Configuration Section
Theconfiguration section is optional in the ReAct sub agent’s config.yml file and provides additional customization options for LLM and connection settings.
configuration.llm: Defines which Large Language Model (LLM) powers the ReAct sub agent’s reasoning and tool use. The configuration format matches the standard used for other Rasa LLM settings. For more information, refer to the LLM Configuration documentation. If not specified, the default OpenAI chat model (gpt-5.1-2025-11-13) is used.configuration.prompt_template: Path to a Jinja2 template for customizing the prompt of this ReAct sub agent. By default, Rasa uses the shipped template for your agent type (mcp_open_agent_prompt_template.jinja2for general-purpose,mcp_task_agent_prompt_template.jinja2for task-specific). Conversation history is not part of that template: Rasa renders the template as the system message and appends recent user and assistant utterances as separate chat messages when calling the LLM.configuration.timeout: Specifies how many seconds to wait before timing out autonomous sub agent execution. There is no timeout by default.configuration.max_retries: Number of retry attempts if MCP server connection fails. The default is 3 attempts.configuration.include_date_time(optional, default:true): Enable or disable datetime information in prompts. When enabled, prompts include current date, time, and day of the week to help the model understand temporal references.configuration.timezone(optional, default:"UTC"): A valid IANA timezone name (e.g.,"America/New_York","Europe/London","Asia/Tokyo").configuration.enable_filler_messages(optional, default:true): Enables intermediate assistant messages for ReAct sub-agents. When enabled, the model includes a short text response alongside tool calls, which is sent to the user before tool execution to reduce perceived latency. Rasa logs this as a BotUttered event with filler metadata to keep the dialogue state consistent. Disable to skip intermediate messages and related metadata.configuration.tool_timeout(optional): Maximum time in seconds for a single tool invocation.
sub_agents/stock_explorer/config.yml
Timezone FormatThe
timezone parameter must be a valid IANA timezone name. Common examples include:"UTC""America/New_York""Europe/London""Asia/Tokyo"
ValidationError during agent initialization.Connections Section
Theconnections section is mandatory for ReAct sub agents and specifies which MCP servers the ReAct sub agent should connect to. MCP servers are configured in your endpoints.yml file. For more details, see the MCP Server Integration page.
connections.mcp_servers(required): A list of one or more MCP servers that this ReAct agent connects to. At least one MCP server must be specified, and each server configuration follows this structure:name(required): Name to reference this MCP server- Tool Filtering (optional):
include_tools: List of specific MCP tools available to this sub agentexclude_tools: List of tools to explicitly hide from this sub agent (useful for restricting certain actions)
include_tools and exclude_tools parameters are mutually exclusive.
To send per-conversation secrets on outbound MCP tool calls, configure pre_call_hook on the MCP server section in endpoints.yml. Rasa invokes the hook before each tool call from this sub agent and merges the result into MCP _meta. For custom Python tools defined on the sub agent itself, see Call-time credentials for custom tools below.
Intermediate messages
Rasa uses intermediate messages or filler messages for user-visible updates while a sub agent is still working. For ReAct sub agents, intermediate messages are currently implemented as tool acknowledgements: brief agent message that the model emits in the same LLM response as its tool calls. Rasa forwards that text to the user before MCP or custom tools run. Default behavior:enable_filler_messages defaults to true. The built-in Jinja2 prompt adds a “Tool Usage Acknowledgments” section that tells the model to keep those intermediate lines short, avoid repeating the user’s request, and vary wording.
Delivery: Intermediate message text uses the same path as other generative assistant text from the ReAct loop. Rasa checks the output channel’s streaming capabilities: when the output channel supports streaming, the LLM completion runs in streaming mode and each text delta is forwarded immediately to the output channel. When streaming is not supported, Rasa uses a non-streaming completion and sends the full text once. Voice-oriented channels can apply slightly longer silence after an intermediate message before the next assistant message so pacing sounds natural.
Configuration: Set enable_filler_messages: false under configuration in the sub agent’s config.yml to disable intermediate-message prompt instructions and filler-specific event metadata.
For more context on streaming and channels, see Messaging and voice channels and Speech integrations.
Custom intermediate messages
ReAct sub agents have access to the output channel, so you can send additional intermediate messages beyond the default model-driven ones—for example a fixed “Working on it…” line at the start of a turn. To send custom intermediate messages, override thesend_message method in your custom ReAct sub agent class:
send_message override (intermediate messages)
Resume after interruption
On resume, Rasa invokes the ReAct sub agent again with the current conversation context. The default prompt adds a system instruction that the run was interrupted and that the agent may use the new context or ask again. If you use a custom prompt template, you can branch onresumed_after_interruption and use resumed_last_request when you need the last agent message from before the interruption.
Customization
ReAct sub agents can be customized in two ways:Modifying the Prompt Template
You can customize the prompt template of the sub agent to:- Include any logic for how the sub agent should operate.
- Pass extra context via slots. You can mention any slot in the prompt template via the
slotsnamespace, for example:{{ slots.time }}. - Access datetime information via the
current_datetimevariable (available wheninclude_date_timeis enabled). This is a datetime object that you can use with methods likestrftime(),time(),tzname(), etc. For example:{{ current_datetime.strftime("%d %B, %Y") }}.
Task description (general-purpose)
For general-purpose agents, the string inagent.description in config.yml is passed into the default prompt as {{ description }} under Primary Task (or whatever heading you use in a custom template).
That string is the main place to define what “done” means for the run (scope, tone, required outcomes).
To change how that text is framed or combined with other context, edit configuration.prompt_template and adjust the Jinja around {{ description }}.
You can also pull in slots (for example {{ slots.my_slot }}) or datetime context as described above.
Default Prompt Templates
Rasa Pro ships two default Jinja2 templates—mcp_open_agent_prompt_template.jinja2 for general-purpose agents and mcp_task_agent_prompt_template.jinja2 for task-specific ones. The tabs below show the complete shipped templates.
When Rasa renders a template, it passes in variables such as description, slot_names (task-specific only), current_datetime, enable_filler_messages, resumed_after_interruption, resumed_last_request, and restarted. Do not embed conversation history in the Jinja: Rasa uses the rendered output as the system message and adds recent user and assistant turns as separate chat messages in the same LLM request.
- General-purpose ReAct sub agent
- Task-specific ReAct sub agent
mcp_open_agent_prompt_template.jinja2
Customizing the Runtime Engine
Runtime customization for ReAct sub agents — whether general-purpose or task-specific — follows the same pattern. Extend the appropriate base class (MCPOpenAgent or MCPTaskAgent, both inherit from MCPBaseAgent) and override the hooks you need.
Register the custom class in configuration.module (see configuration file).
Customization hooks reference
Most customizations only need the hooks below. Prefer public methods; do not override private helpers (names starting with_).
Output processing for ReAct sub agentsReAct sub agents do not use
process_agent_output (that hook is for external A2A agents only). To integrate tool or agent results back into the tracker, override process_tool_output or return SlotSet events from custom tool executors.- General-purpose ReAct Sub Agent
- Task-specific ReAct Sub Agent
To create a custom general-purpose ReAct sub agent, inherit from Customizing the
Tool schema and descriptionOverride the static method When the model calls
MCPOpenAgent:MCPOpenAgent subclass (skeleton)
Customizing the task_completed tool and empty final messages
Tool schema and descriptionOverride the static method get_task_completed_tool() to change what the LLM sees (for example stricter or looser closing instructions).
Keep the function name task_completed and a valid function-calling schema so the completion loop still recognizes the tool.
Add a deepcopy of the default tool and replace only the description string, for example on the same subclass as your other overrides:task_completed with no user-facing textIf the assistant turn contains a task_completed tool call but no assistant message text, Rasa does not treat that as the final reply to the user.
It appends a system follow-up instruction and calls the LLM again without tools, asking for a short, natural closing message only.
After that text is produced, the run completes as usual.To change that recovery behavior (tone, length, language), override the static method get_system_message_for_empty_content_at_task_completion() and return your own system message dict with the same shape (role and content keys as in the parent implementation).config.yml (custom module)
Processing input
Overrideprocess_input() to filter which slots reach the agent or to inject derived context before the ReAct loop starts. This runs once per user turn, before send_message():
process_input — filter slots and add context
AgentInput schema.
Adding custom tools
You can add custom tools that are independent of the MCP server but essential for the sub agent to accomplish its task. To define a custom tool, implement it as a Python function. The executor always receives the LLM tool arguments and anAgentToolContext (from rasa.agents.schemas) whose metadata field is a copy of the current request’s AgentInput.metadata (or an empty dict). Use context.metadata for backend-only data such as auth or user context; do not rely on the model to populate those keys—they are not part of the tool schema the LLM sees.
Custom tool executor (example)
arguments parameter contains only the parameters defined on the tool schema. Extract structured values the model should supply from arguments; read orchestration metadata from context.metadata.
Call-time credentials for custom tools
For secrets that must not live in slots orAgentInput.metadata, override build_custom_tool_call_metadata() on your MCPOpenAgent or MCPTaskAgent subclass. Rasa calls it before each custom tool execution and merges the returned metadata into AgentToolContext.metadata. The method may be async def or def; use async def when fetching credentials over the network.
External MCP tools use pre_call_hook on the MCP server in endpoints.yml instead of this method. See Call-time credential hooks.
Import types from rasa.shared.agents.outbound_call_hook. The hook receives MCPOutboundCallContext with sender_id (secret-store lookup key), tool_name, and optional agent_id. Return an OutboundCallResult whose metadata dict is merged into AgentToolContext.metadata:
Call-time metadata for custom tools (example)
AgentToolResult, which includes the following fields:
AgentToolResult fields
get_custom_tool_definitions method. This method should return a list of tool definitions, each adhering to the OpenAI function calling specification. Additionally, each tool definition must include a tool_executor key, which should reference the function to execute when the tool is invoked:
Custom tool definition with tool_executor (example)
MCPOpenAgent:
get_custom_tool_definitions (full example)
Processing tool output
Overrideprocess_tool_output() to convert MCP or custom tool results into tracker events after each successful tool iteration. Returned events are appended to the agent output and included in subsequent LLM iterations within the same turn:
process_tool_output — map tool results to slots