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:. It splits into public: and private:;
both are optional.
Unknown keys are ignored and not used by the engine.
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.
Project memory is shared across skills for the rest of the session. After the
first write it cannot be changed.
Put facts every skill can rely on here (authenticated, a verified customer id,
a seeded caller phone). Keep collectable, correctable values on the owning
skill as schema.public (for example a chosen card label).
How set project values appear in the prompt is documented on
System Prompt and
Prompt templates.
Seeding from session start
If you want to populate a project field from channel metadata, setseed: true.
By default, seed is false: the engine does not copy channel metadata into
project memory unless you opt in.
Use it for channel facts such as caller phone or language. The copy uses the
same name as the field and is the first and only write.
Channel metadata often uses the same names as project fields. A matching name
is not enough: only fields you mark are copied, so a client cannot lock
authenticated (or any other unmarked field) by sending that key.
Here is an example. You have these project memory fields:
memory.yml
caller_phone is set from metadata. authenticated stays unset until a tool
or a memory validation tool writes it.
A missing key, or a value that does not match the field’s type, leaves the
field unset. seed: true on skill public or private fields fails rasa train.
Skill vs project memory
Use project memory for facts every skill should rely on. Use skill memory for values that belong to one task.
The LLM cannot write project fields (
set_fields, collect, correct).
rasa train rejects llm_settable: true and collect: on a project field.
A static set:session.project.<entry>=… button can write a project field
(still write-once).
Typical pattern: collect the user-facing value on the skill, then have that
skill’s tool or run_after_setting_<skill_field> memory validation tool
write project.*.
Completing or reactivating a skill does not clear project values.
If a skill needs a project fact before it starts, declare a semantic
precondition on the skill and bind its
satisfied_when condition and resolver in
agent.yml (orchestrator.preconditions).
Do not declare working state in root memory.yml: write-once would lock the
first value for the rest of the session.
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.
llm_settable is skill memory only. A project field with this flag fails
rasa train.
Static set: buttons are engine writes: they do not need this flag. A tap
can still fill a collect target or another writable field. See
Set-memory payloads.
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.
A skill can write its own declared fields and project fields (project is
write-once). It cannot write another skill’s fields.
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.
Lifetime
Skill public and private memory is working state for the current run, not session-long truth.- After a skill completes or is cancelled, its live values stay. Other skills can still read that skill’s public fields (for example a selected card id). Private fields stay set but are not readable by others.
- When the same skill starts again in the session (it is no longer on the
stack), the engine resets that skill’s public and private fields to
initial_value(ornullif unset). Tracker history still holds the old values. That reset happens before anutter:on: activateresponse is selected, so conditional variants that read this skill’s fields usually see the default wording unless those fields declare a matchinginitial_value— prefer project memory or awhen:trigger for values written after activation (see Conditional variants). - Interrupt and resume do not reset: the parked skill keeps its memory.
project.*is never cleared because a skill finished or started again. Put facts that must last the whole conversation there (authenticated, verified customer id), not in skill public.
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:
In tools, get may use either form in that last column. set may use a
bare name,
project.<entry>, or <own_skill_id>.<entry> only — not another
skill’s fully-qualified name.
Use session.* in structured places (requires:, if:, tool parameters).
Use @memory.* when instruction prose needs the live value inline.
Use {session.*} in response templates. Do not put session.* in free
prose — train fails and suggests @memory.… instead.
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:
project.<entry>, or its own skill
id. project. always writes project memory (even when the skill declares the
same name). <own_skill_id>. writes that skill’s field only (no project
fallback). Another skill’s fields cannot be written here:
system.* is reserved for engine internals and can never be declared.
@memory.system.* is not supported.
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.* / @memory.card_replace.*.See also
- Memory concept: when to declare what
- Conditions: referencing entries in
requires:/if: - skill.md:
@memoryin instruction prose - Tools reference:
context.memoryread/write rules