memory.yml declares what a skill remembers. Every value a tool writes must be
declared here (or in the project-level file): rasa train rejects an
undeclared write.
There are two kinds of memory.yml, with different shapes.
Skill memory: skills/<id>/memory.yml
skills/card_replace/memory.yml
schema: and access:. schema: splits into
public: and private:; both are optional.
Project memory: memory.yml at the agent root
Values shared across skills live in a project-level file. Its shape is
different: a flat map of entry name to attributes, with no
schema:/public:/private: wrapper.
memory.yml
project. namespace, so a condition refers to
session.project.authenticated. This is the mechanism behind cross-skill state:
one skill writes authenticated, another gates on it, and neither references
the other.
Field attributes
Every attribute is optional; a bare entry (my_field: {}) is a valid any field.
Types
Write the type name exactly as it appears in the left column.
enum_values
categorical field with enum_values gets an enum constraint in the
set_fields schema, so the LLM can only record one of the listed values. It is
also what makes the field usable in if: markers with confidence about the
value space.
llm_settable
This is the switch that decides whether the LLM may write a value at all.
- fields flagged
llm_settable: true, and - fields owned by a
collect:step in the active skill (settable regardless of the flag. The engine asked the user for them directly).
llm_settable also gates whether a field is offered to the correct tool. Leave
engine-derived values (eligibility results, lock state, computed flags) at the
default so the model can neither set nor “correct” them.
A project field with
llm_settable: true is settable from any skill. A
project field owned by a collect: step is settable only while its owning skill
is active.Visibility
Concretely, while skill
A is active it can read A’s own public and
private fields, every other skill’s public fields, and all project fields,
minus anything listed in A’s deny_read.
public is your skill’s API. Keep it small and stable: another skill gating on
session.project.authenticated should depend on that key, not on the auth
skill’s internals.
Access control
<skill_id>.<entry> or
project.<entry>.
- A denied read returns
None. It does not raise. - A denied write raises
MemoryWriteError.
Undeclared writes
context.memory.set("foo", 1) where foo is in neither the skill’s schema:
nor the project file fails rasa train with undeclared_memory_write. Fix it
by declaring the entry with its type.
The same applies to a collect: step target and to any key named in a
requires: or if: condition.
Fully-qualified names
The scope an entry is declared in determines its full name:
While skill
card_replace is active, a tool reads its own field with the bare
name and another skill’s public field with the fully-qualified name:
system.* is reserved for engine internals and can never be declared.
The namespace is the skill directory name, not the
name: in frontmatter. A
skill in skills/card_replace/ with name: Card Replace is always
session.card_replace.*.See also
- Memory concept: when to declare what
- Conditions: referencing entries in
requires:/if: - Tools reference:
context.memoryread/write rules