Skip to main content
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
The top-level key is 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
These resolve to the 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, set seed: 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
If the channel sends this metadata at connect:
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

A 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.
The entries the LLM may set for a turn are the union of:
  • 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 (or null if unset). Tracker history still holds the old values. That reset happens before an utter: on: activate response is selected, so conditional variants that read this skill’s fields usually see the default wording unless those fields declare a matching initial_value — prefer project memory or a when: 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:
The same tool writes with a bare 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