Skip to main content

Skills

Voice and chat agents in Rasa are built from composable skills, coordinated at runtime by an orchestrator called Maestro. Reliability at scale comes from progressive control: each skill starts as plain instructions, and you add deterministic guarantees only where the business needs them.

This page builds one skill, card_replace, from a single file to a constrained procedure. Scroll to follow along.

Start with skill.md

A skill is a folder: instructions for the LLM, plus optional tools and references. At its simplest, the folder holds one file. Frontmatter with a name and description, then instructions in plain language. The folder name is the skill id; see Project structure for the full layout.

No flowcharts, no state machine. The LLM interprets the instructions in conversation.

Add tools/

Define tools in a tools/ subfolder and they are automatically available while the skill is active. No registration. A tool returns structured data to the LLM and can store results in memory for later steps.

Add references/

FAQs, policies, and other reference material live in a references/ subfolder. They are indexed at training time and retrieved on demand, so the agent can answer from them without carrying them in every prompt.

Progressive control

Everything so far is instructions an LLM interprets. Where a mistake is expensive, you constrain it, with tool constraints, skill prerequisites, or ordered blocks.

These are framework guarantees enforced by the runtime, not suggestions to the model. A tool whose constraint isn’t met is not available for the LLM to call; a jailbreak can’t change that.

Tool constraints

A requires condition in the frontmatter gates when a tool becomes available. Until selected_card_id exists in memory, lock_card is removed from the tool schema entirely. The model can’t call a tool it never sees.

Enforced before the tool call executes

Scoped instructions

Different situations need different instructions. if: markers scope a paragraph to a memory value: when replacement_reason is stolen, only the stolen paragraph is visible to the LLM. Non-matching branches are stripped from the prompt entirely.

Non-matching branches removed from prompt

Skill prerequisites

A requires block at the root of the frontmatter gates the whole skill: Maestro won’t activate card_replace until authenticated is true. The only contract with the authentication skill is the memory key.

Checked before the skill activates

Ordered blocks

For steps that must run in exact order, add an ordered_block to the instructions. The LLM decides when to invoke @block.pick_card; the runtime executes the steps inside it in order.

Step order enforced by the runtime
card_replace/
skill.md
tools/tools.pyOptional
references/faqs.mdOptional
card_replace/skill.md
name: Card Replacedescription stolen, damaged, or not receivedHelp the customer replace a credit card.Check whether their account is eligible forreplacement. If they have multiple cards, askthe valid reasons are lost, stolen, damaged,or not received.For stolen or not-received cards, offer to lockthe card while a new one ships. Once everythingis gathered, ask their shipping preference,confirm the order, and process the replacement.
card_replace/tools/tools.py
from rasa.calm_v2.tools.decorator import ToolContext, toolfrom rasa.calm_v2.tools.result import ToolResult@tool(description=)async def list_cards( account_id: str, context: ToolContext = None,) -> ToolResult: cards = await call_cards_api(account_id) # Store in memory for later steps context.memory.set(, cards) # Structured data goes back to the LLM return ToolResult( llm_response={ : cards, : len(cards) }, )
card_replace/references/faqs.md
Q: What if my dog ate my card?A: That counts as damaged.Q: Will my replacement card keep the same number and PIN?A: The number may change; your PIN stays the same.Q: How long does delivery take?A: 5-7 business days, or 2 with express shipping.
card_replace/skill.mdhard guarantee
name: Card Replacedescription stolen, damaged, or not receivedtool_constraints: - lock_card: requires: Help the customer replace a credit card.Until selected_card_id is set, lock_card isnot in the tool schema at all.
card_replace/skill.mdscoped
damaged, or not received.if: Ask who needs a replacement: just theif: Tell the customer the card will be locked.Confirm, then lock the card.if: Present recent transactions and ask if theylook familiar. Then handle locking.
card_replace/skill.mdhard guarantee
name: Card Replacedescription stolen, damaged, or not receivedrequires: tool_constraints: - lock_card: requires: Checked by the runtime before the skillactivates.
card_replace/skill.mdexact order
## Identify the reasondamaged, or not received. Once the reason iscollected, invoke @block.pick_card:::ordered_block id=pick_cardsteps: - id: check_eligibility execute_tool: card_replace_eligibility - id: fetch_cards execute_tool: get_customer_info - id: select_card instructions them pick one complete_when: - id: END:::

If a whole skill must follow a strict procedure, replace the entire body with an ordered block. Start simple, add control incrementally, and build fully deterministic skills where you need them.

Local Quickstart

Build a skill and run it. 2 minutes.

Build Guide

The progressive control spectrum, one lever at a time.

Orchestration

How Maestro runs a turn and what it guarantees.

Reference

File formats, tools contract, runtime internals.