Skip to main content
Every skill you write gets read by a model twice:
  1. Choosing. Your skill’s description sits in a list next to the description of every other skill, with one customer message to go on. The model picks one skill.
  2. Working. Your skill is now running, and the model reads the body to decide what to say and what to do next.
Two readings, two jobs. This page covers writing for each.

The description decides which skill gets picked

The routing list the model reads is built from two things per skill: the skill id, which is the folder name, and the description. The name field is a display label and takes no part in choosing, so the description is where the work happens.

Name the thing first, then the action

Lead with the subject the skill covers, then say what it does with it:
The opening words carry the most weight. The subject is what tells your skills apart, and the action is usually what they share, so the subject is what belongs in front. Separate the two halves with a dash. A colon reads well too, but a colon has meaning in YAML, so a description written with one has to be wrapped in double quotes to load at all. A dash needs no quotes and puts the same word first. When the action comes first instead, it pulls routing toward itself. Suppose the same skill opened with the action:
A customer asks:
How do I replace my PIN?
The model opens the card replacement skill. Nothing about PINs lives in that skill, but the question and the front of the description both lead with “replace”, so that is what matches. With the subject in front, the same question falls through to the knowledge base, which is where the answer is. The same ordering helps a tool description, which the model reads the same way when it is choosing which tool to call:

Use the words your customers use

Fill the description with the phrasings a real customer would type or say:
“lost, stolen, damaged, or not received” earns its place, because each one is a different way a customer arrives at this skill. Add a phrasing when it brings a genuinely different case. A second word for a case you already cover, like “misplaced” next to “lost”, takes up room without telling the model anything new.

Leave out what the skill id already says

The routing list already shows the skill id, so words that repeat it buy nothing. For a skill in skills/travel_notice/:
Compare that with a description that opens by repeating the id:
Both are one line, but only the first spends it on “adding”, “removing”, “travel dates” and “abroad”, which are the words a customer’s message might match.

The body tells the model how to do the job

Once a skill is running, the body is what the model follows.

Brief it like a new teammate

Write the body the way you would explain the job to someone competent who is new: what to find out, what to offer, and in what order.
skills/card_replace/skill.md

Number the steps that have to happen in order

Use a numbered list when each step depends on the one before it:
Use bullets when the pieces are independent, so the model is free to take them in whatever order the conversation reaches them:
Where order is free between two things that sit inside a numbered run, say so in a sentence: “The delivery address and the contact number can be checked at any point.” That way nothing is left to be guessed at.

Give a long body headings

A short body reads fine as a few paragraphs. Once it covers several stages, put each one under its own ## heading:
Headings give you somewhere obvious to put each instruction, which keeps related lines together instead of scattered down the body. They also make the skill easier to revise later, because you can see which stage a change belongs to. A skill that needs no headings is usually a skill that is the right size.

Put a rule where its cost matches its reach

A rule can sit in three places, and they differ in how often the model is carrying it: Put a rule in agent.yml when it holds everywhere, such as the language to answer in. Put it at the top of a skill body when it holds for that job only:
A rule at the top of a long body is far from the step it governs, so restate it at the point it applies when it matters. That is the one place repeating yourself pays.

Say what to achieve, not which words to use

Describe the outcome and let the model find the wording. It adapts to how the customer is talking, which is most of the value of writing prose at all. When the wording has to be exact, for legal or compliance reasons, put it in responses.yml and it is delivered verbatim.

Say what you want to happen

Write the behaviour you want. Naming a behaviour to avoid puts that very thing in front of the model, and the “never” is a weak word wrapped around a strong one, so the ban half-reads as an instruction to do it. Most prohibitions have a positive form that is also more useful, because it tells the model what to do instead: A prohibition earns its place only as a hard guardrail you cannot phrase positively. Pair it with the positive target when you write one, so the model still has something to aim at.

Point at things with @

Four kinds of thing can be named in the body, and they share one shape. Each takes an id, never a display name. Tools. While a skill is running, its tools are already available to the model, so naming one is enough:
Ask for their account number, then call @tool.check_balance.
Memory. The namespace is either a skill id or project, and there are always exactly two parts after @memory.:
Confirm the card ending in @memory.card_replace.selected_card_last_four is the right one.
This is the form for the body. Paths like session.card_replace.… belong in conditions such as requires: and if:. Another skill. Use the folder name under skills/, which is the skill id, rather than the name: label:
Once the card is chosen, run @skill.card_disambiguation to settle which account it belongs to.
The other skill runs, and this one carries on where it left off when that finishes. That return is guaranteed. Write the reference when this skill still handles the request, and leave it out when the user changes the subject. See Delegate a part of a skill to another skill with @skill. An ordered block. Use the id you gave the fence in :::ordered_block id=<block_id>:
Once the reason is recorded, work through @block.pick_card.
The block runs its steps in order, then hands back to the prose that called it. rasa train resolves @memory references against the memory this skill can read, and @block ids against the blocks this skill declares, so a typo in either surfaces at build time. The referenced skill has its own task. Write @skill.<skill_id> when the skill you are writing still handles the request, including the part the other skill does. That is a delegation: Mantle runs the other skill and brings this one back when it finishes. When the user asks for a task this skill does not handle, leave the reference out. Mantle parks this skill and offers them a resume. For example, a complaint about the replacement fee is still the replacement:
skills/card_replace/skill.md
A balance question during the same replacement, however, is a task this skill does not handle. Card replacement does not need the balance to finish, so there is nothing to delegate. Leave @skill.account_balance out of the card-replacement body. Mantle treats the question as a topic change, parks the replacement, and offers the user a resume once the balance is answered. How a delegation and a topic change park and resume the parent is in skill.md.

Let the tool result carry what happens next

The model sees what a tool returned, so write what to do with the result:
Once you have their cards, show the last four digits of each and ask which one needs replacing.
Describing the shape of the result, such as which fields it contains, repeats something the model already has in front of it. When an instruction only applies after a particular tool has run, the result itself is a good place for it. Return it as a field alongside the data:
The model reads that on the turn the tool ran, and it costs nothing on every other turn, which is what the same sentence in the body would do.

Reference