Memory¶
Memory is what an agent keeps from one conversation to the next: a fact about a customer, a preference, a promise, the way a task is done. Core has no separate service for it. Each kind of memory lives in a part of Core you already know, and for each part the same question applies: does this belong to one person, or to the whole organisation?
What Core remembers, and where¶
| What you want remembered | Where it lives | Who writes it |
|---|---|---|
| Facts and preferences, such as "prefers email to phone" | memory records in a memory store, with a type you choose | your application with POST /memory, the model with the write tool, or Core itself from your conversations |
| Rules for how the assistant should act for one person, such as "reply in Dutch" | memory records of type behaviour, hers alone, given to every turn for her |
the same three |
| Commitments, such as "calls the bank back on Thursday" | memory records of type commitment, with a status, a due time and who it is owed to |
the same three |
| How a task is done | skills, an agent's instructions and prompts | you or an administrator, in the organisation's library |
| What happened in a conversation | the conversation itself, with a summary once it grows long | Core, on every turn |
| What happened in past sessions | memory records of type episode, one for each settled session, found again by what a new turn is about |
Core itself, from your conversations, when the store's extraction asks for it (Keep a record of each session) |
| What a person's own sessions taught | memory records of type lesson, hers alone |
Core itself, from her own alike sessions, when the store asks for it (Let a person's sessions teach her assistant) |
| The task in hand: its goal, the decisions taken, the next step | the conversation's working state, one small JSON object shown at the start of every turn, see Keep a working state | the model with builtin__update_working_state, or your application |
Every write, the model's and extraction's included, needs a rule in the key's policy that permits
memory_write; the default rules hold none (Write your first
rule).
A turn gets memory in two ways. The model can search with a memory store's search tool. Or Core adds matching records to the context before the model starts, when you switch that on for the key. To the model an added record is always data, never an instruction. How to set up both is in Memory through the API.
Whose record it is: a person's or the organisation's¶
A memory store holds two kinds of records side by side:
- Personal records. When a request names a person, with
X-Core-Subject, a subject token or theuserfield, every record it writes belongs to that person. "Jan prefers mornings" is Jan's. - Shared records. A request that names nobody writes a record that belongs to the organisation. "Expense claims go to finance by the 25th" is everyone's.
What Core extracts from conversations follows the conversation: a conversation of Jan's gives
Jan's records. By default extraction reads only conversations that name a person (threads is
with_person), so a conversation that names nobody gives no records at all.
Under a key's default rules a person sees her own records and the shared ones. Piet never sees
Jan's records: not in a list, not in a search result and not in what Core adds automatically, and
a read by id answers 404. A request that names nobody, such as a background job of your
application, reaches every record. What Core adds automatically is narrower: a turn for Jan gets
his own records and the shared ones, in separate sections of one block, and when the store asks for them his own past sessions under [past_sessions], so many shared records never
crowd out his own, and a turn that names nobody gets the shared ones only. Every record shows its
confidence as a percentage, and the block is held to a token budget: the key's
memory.injection.budget_tokens (1,500 by default), which a store's optional
injection.budget_tokens and your operator can only lower. An agent can bring its own injection
block, with its own store and budget, which then replaces the key's for its turns.
A personal record never becomes shared by itself. A correction keeps the fact with the same owner,
and Jan's subject token cannot change or delete a shared record (403).
Always name the person. A request that names nobody counts as a request of the organisation. If
your application forgets to send the person, Jan's conversation becomes a conversation for nobody.
With threads: with_person, the default, Core does not mine that conversation. A switch whose
GET shows threads: all still mines conversations for nobody, so his personal facts become
shared records that all his colleagues are given; a PUT without threads sets with_person.
Set settings.subjects_required to true in the policy of every key that works for people, and a
request without a person is refused (What a request without a subject
reaches). You can also limit
extraction with filters.key_ids to the keys that always name a person.
Departments: labels on shared records¶
Core has no departments. You make them with a label. A shared record for payroll carries
"labels": {"department": "payroll"}, and a shared record for everyone carries
"labels": {"audience": "everyone"}. A shared record the improvement loop proposes starts with
the store's promotion.labels in its metadata, which these rules read only during the
move to labels. The person carries their department as an attribute on
the request, for example department: ["payroll"].
In the key's policy you then replace the default rule memory-record-shared with two rules that
permit shared records only:
{"id": "memory-record-everyone", "effect": "permit", "kind": "memory_record",
"match": {"labels": {"subject": "", "audience": "everyone"}}},
{"id": "memory-record-department", "effect": "permit", "kind": "memory_record",
"match": {"labels": {"subject": ""}},
"when": [{"atom": "label_among", "label": "department", "attribute": "department"}]}
"subject": "" means a record nobody in particular wrote. The rules memory-record-nobody-named
and memory-record-own stay. Anna from payroll then gets her own records, the records for
everyone and payroll's. Bob from reception gets his own records and the records for everyone.
That holds for lists, searches and automatic injection alike.
Four things to watch:
- A rule cannot say "everything except". A record for the whole organisation must therefore carry
audience: everyone. A shared record with neither label is reached only by a request that names nobody. Records Core extracts from conversations carry neither label, so a shared record extracted from a conversation for nobody (only possible withthreads: all) is reached only by such a request too. - Both rules are about shared records only. A label somebody puts on her own record does not show that record to colleagues.
- A schedule never carries attributes. When it names a person, its turn gets that person's records and the records for everyone, and no department's. When it names nobody, its turn gets every shared record, every department's included.
- If your organisation declared a
vocabulary, declare
departmentformemory_recordwithattribute: true, andaudienceformemory_record.
You divide skills by department the same way: a skill labelled department: payroll under a rule
with label_among over skill is offered to that department only. How to write a rule is in
Write your first rule.
Erase a person¶
DELETE /subjects/{id} erases a person's conversations and all her memory records, of every type
(Erase a person). Shared records stay:
they belong to the organisation and name nobody.
Read on¶
- Memory through the API: write and read records, the tools, automatic injection and extracting memory from conversations.
- Memory in the console: view records, stores and commitments.
- Build for people: subjects, attributes, labels and rules.
- Conversations: what a conversation keeps, and how it is summarised.
- Skills through the API: fixed ways of working for an agent.