Skip to content

One store for the desk, or one for each machine

Lesson 1 called enable_for_agent with no scope. That took the default, and the default is one store for the whole agent:

AGENT_CODE SCOPE CONTEXT_KEY ENABLED
MX_DESK agent Y

One store, six machines. Every conversation about every machine reads and writes the same virtual filesystem.

A store is one virtual filesystem, and a scope decides how many stores there are.

The demo data holds a pair that a plant has.

AssetAlarmSeverityIs it real?
AS-14 Syrup feed pumpPRS-LOWLOWNo. It trips on every clean-in-place cycle and clears itself.
AS-31 Plant air compressorDIS-PRS-LOWHIGHYes. 4.9 bar against a 6.5 bar setpoint, and the whole line is short of air.

Different assets, different alarm codes. Nothing in the data connects them.

Block 1 of 02_stores.sql records the true fact about the pump. Nadia says:

About the low pressure alarm on this one: it is a nuisance. It trips on every clean-in-place cycle and clears itself, and nobody should be chasing it. Write that down so the next shift does not waste an hour on it.

Then block 2 is a conversation about the compressor, from a different technician:

I have a low pressure alarm on this machine. Do I need to act on it, or is it one we ignore?

The compressor answergpt-5.6-terra2026-08-25

Act on it — do not ignore it.

AS-31 has a high discharge-pressure alarm: 4.9 bar vs 6.5 bar setpoint, and the line air is starved. The inlet filter was already found clean; the open investigation calls for checking the unloader valve. Also monitor the concurrent high oil temperature (92 °C).

Your wording will differ. What must match is the data, and the checks below.

The answer names the compressor alarm and the unloader valve.

Now read the trace instead of the answer. Every MEMORY call of that one run:

select m.seq
, json_value(m.tool_input, '$.command') as command
, json_value(m.tool_input, '$.path') as path
from uc_ai_agent_messages m
where m.tool_name = 'MEMORY'
and m.role = 'tool_call'
and m.session_id = :sess_31
order by m.seq;
SEQ COMMAND PATH
3 view /memories
5 view /memories/as-14-handover.txt
13 create /memories/as-31-context.txt

A run bound to the compressor opened the pump’s handover file and read it.

The model’s file names are the only thing that separated the two machines above, and the model chose those names. That is not a control. The store key is.

select f.store_key
, f.scope
, f.path
from uc_ai_v_memory_files f
where f.agent_code = 'MX_DESK'
order by f.path;
STORE_KEY SCOPE PATH
agent:MX_DESK agent /memories/as-14-handover.txt
agent:MX_DESK agent /memories/as-31-context.txt

One key, both machines. And resolve_store_id for this scope takes no asset, because there is nowhere to put one:

l_store_id := uc_ai_memory.resolve_store_id(
p_scope => uc_ai_memory.c_scope_agent
, p_agent_code => 'MX_DESK'
);
The store every run of MX_DESK resolves to: id=3757

Every run of this desk resolves to that one number, whatever machine the technician is standing at.

The fix is the same call as lesson 1, with a scope and a key:

begin
uc_ai_memory.enable_for_agent(
p_agent_code => 'MX_DESK'
, p_scope => uc_ai_memory.c_scope_context
, p_context_key => 'asset_no'
);
commit;
end;
/
AGENT_CODE SCOPE CONTEXT_KEY ENABLED
MX_DESK context asset_no Y

The context scope keys the store on a value from the run context. Every run of this course passes p_run_context => json_object_t('{"asset_no":"AS-14", ...}'), and that is the same value that binds the two read tools. It now does a second job.

The call overwrites the configuration row. It does not add a second one, so there is no old scope still in force somewhere.

It changed one configuration row. A store is a row of its own, and the files hang off the store. So the old shared store is still there, with everything in it:

STORE_KEY SCOPE PATH CHAR_COUNT
agent:MX_DESK agent /memories/as-14-handover.txt 1190
agent:MX_DESK agent /memories/as-31-context.txt 525

The agent no longer selects the old store, but its files remain. Run this block to delete them:

declare
l_store_id number;
e_no_store exception;
pragma exception_init(e_no_store, -20423);
begin
l_store_id := uc_ai_memory.resolve_store_id(
p_scope => uc_ai_memory.c_scope_agent
, p_agent_code => 'MX_DESK'
);
uc_ai_memory.clear_store_files(l_store_id);
commit;
exception
when e_no_store then
null; -- there was never a shared store
end;
/

ORA-20423 means the store does not exist. UC AI creates a store at its first use, so a script that clears a memory before anything has run must accept that error.

Block 6 runs both questions again. The answers are the same as before, and the store list is not:

STORE_KEY PATH CHAR_COUNT
context:MX_DESK:asset_no:AS-14 /memories/AS-14-operating-note.txt 430
context:MX_DESK:asset_no:AS-31 /memories/current_asset.md 494

Two machines, two keys. The key has a fixed shape, context:<agent>:<key>:<value>, so an operator can read or delete the memory of one machine from that string alone.

Do not ask the model. Ask the AS-31 store for a file that lives in the AS-14 store:

l_store_31 := uc_ai_memory.resolve_store_id(
p_scope => uc_ai_memory.c_scope_context
, p_agent_code => 'MX_DESK'
, p_context_key => 'asset_no'
, p_context_value => 'AS-31'
);
l_content := uc_ai_memory.get_file(l_store_31, '/memories/AS-14-operating-note.txt');
AS-14 wrote: /memories/AS-14-operating-note.txt
Reading it from the AS-31 store (id=3759) ...
ORA-20425: not in this store. The machines are separated.

That is a deterministic check. It gives the same result on every run, on every model, and with the provider switched off.

  • The default scope is agent, which is one store for every record the agent ever works on. It is right for a desk that accumulates general knowledge, and wrong for a desk that works on one record at a time.
  • Read the trace, not the answer. A run that gives a correct answer can still have opened a file that belonged to another record.
  • A change of scope does not move or delete the files in the old store. Clear it yourself.
  • uc_ai_memory.enable_for_agent(p_agent_code => 'MX_DESK', p_scope => 'context', p_context_key => 'asset_no');

Full reference: Scopes: which agents share which memory lists all six, including user, which keys on the signed-in user and not on your records.