Skip to content

tutorial

Chapter 5 of 6

Chapter 5 — Public and private skill memory

by Rod Rivera Published

Skill-scoped state, split into what other skills may read and what never leaves the skill — enforced by the runtime.

Not everything belongs in project memory. Most of what a skill tracks is its own working state: the account being asked about, the amount being sent, how many passphrase attempts have failed.

That is skill memory, declared in the skill’s own folder and split in two.

# skills/transfer_money/memory.yml
schema:
  public:
    amount:
      type: float
      description: Amount the caller asked to send.
      llm_settable: true
    payee_name:
      type: text
      description: Who the money is going to.
      llm_settable: true
    transfer_confirmed:
      type: bool
      description: Caller explicitly confirmed the transfer details.
      initial_value: false
      llm_settable: true
  private:
    transfer_reason:
      type: text
      description: Free-text reason the caller gave for the payment.
      pii: true
      llm_settable: true

The top-level key is schema:, not memory:.

Public fields are readable by every other skill. Private fields are readable only by the skill that owns them. transfer_reason is free text the caller volunteered, flagged pii: true, and it stays inside Transfer Money.

The boundary is enforced by the runtime. It is not a convention or a lint rule, and the model cannot talk its way past it — a denied read simply returns nothing.

Two different writers

Two actors write memory, and they are governed separately:

  • Tools write whatever the skill declares, through context.memory.set().
  • The model may only write fields marked llm_settable: true, through the built-in set_fields tool.

Mark the caller’s own decisions settable — the amount, the payee, the confirmation. Leave anything the system derives to tools. passphrase_attempts is not settable by the model, because the model has no business deciding how many times you failed.

Everything must be declared

A tool that writes an undeclared field fails at rasa train:

A tool in skills/authenticate/tools.py writes undeclared memory entry
'project.customer_id' via memory.set(...) at line 38. Declare it under the
skill's schema (public/private) or the project memory.yml, or it will be
rejected at runtime.

This is a good failure. It happens at build time rather than three turns into a production conversation.

The description rule that will catch you

Validation rejects a description on a field that is neither llm_settable nor owned by a collect: step:

Memory field 'verification_method' in skill 'authenticate' declares a
description but is neither llm_settable nor collect-owned. Descriptions are
only surfaced on set_fields for LLM-facing entries — remove the description or
mark the field settable.

The reasoning: a description exists so the model knows what to put in a field. If the model can never write it, the description is documentation aimed at nobody, and Mantle would rather you say so in a comment. Use a YAML comment for tool-written fields.

Choosing a scope

Ask Scope
Is it about the session or the end user? Project
Is it this skill’s working state that others may need to see? Public
Is it internal, sensitive, or meaningless outside this skill? Private

When in doubt, start private. Widening a field later is a one-line change; discovering that three skills quietly depend on something you meant to keep internal is not.