Skip to content

Agent Memory

Agent memory lets an agent store and read knowledge across conversations. The agent gets a private virtual filesystem with the root /memories. CLOBs in Oracle tables hold this filesystem. A memory tool can view, create, str_replace, insert, delete, and rename the files in it. What the agent learns in one session is available in the next session: user preferences, decisions, and task progress.

The tool implements the contract of the Anthropic memory tool. Claude models are trained on this command set. The tool is a normal UC AI function tool, so it works with every provider: OpenAI, Google, OCI, Ollama, and the others.

The installer of UC AI creates the MEMORY tool row, so no tool setup is necessary. One tool row serves every agent, because the tool resolves the store of the caller at each call.

begin
uc_ai_memory.enable_for_agent('SUPPORT_AGENT');
end;
/

This one call does three things:

  1. It creates the row of the agent in uc_ai_memory_config, with the scope and the size caps.

  2. It adds the memory tool tag and g_enable_tools to the model_config_json of the prompt profile of the agent. The tool then reaches the model.

  3. From the next new session, UC AI appends the MEMORY PROTOCOL block to the rendered system prompt of the agent. This block says: always view /memories first, record progress during the work, and expect an interruption.

The agent now reads its memory at the start of a conversation, and records what it learns. Every file is plain text. A directory exists only through the path, like a key in an object store.

Every memory file lives in a store, and one store is one virtual filesystem. You configure the store of each agent:

ScopeOne store per…Use for
agentagent code (default)agents that accumulate knowledge. All users share the memory
useragent + created_byone memory per signed-in end user (read the note below)
sessionconversation sessionscratch memory that must not outlive the conversation
sharedexplicitly named storeseveral agents that work on one knowledge base
globalwhole installationone common memory for everything
contextvalue of a run-context keyone memory per document, per case, or per tenant

A memory command reports an error as text, because the model must be able to read it. That is correct for “the path does not exist”: the model reads it as an empty memory and continues.

A store that cannot be resolved is different. It is a failure to reach the data, but it arrives on the same channel, and a model reads it the same way. It then tells the user that nothing is recorded, while the content is in the store and the run reports success. The user gets a confident, wrong answer, and the trace looks healthy unless you read tool_output.

UC AI therefore fails such a run at the start, before the model sees anything. The developer gets the error, the end user does not get the wrong answer. For a failure that can only appear during a run, such as a store deleted mid-run, the MEMORY PROTOCOL tells the model to report that the memory could not be read instead of describing what it remembers.

-- per-user memory
uc_ai_memory.enable_for_agent('SUPPORT_AGENT', p_scope => 'user');
-- two agents sharing one named store
uc_ai_memory.enable_for_agent('RESEARCH_AGENT', p_scope => 'shared', p_store_code => 'TEAM_KB');
uc_ai_memory.enable_for_agent('WRITER_AGENT', p_scope => 'shared', p_store_code => 'TEAM_KB');

UC AI creates a store at its first use. It resolves the store at tool-call time from the execution context. One tool row therefore gives every agent and every user a separate filesystem.

The context scope keys the store on a value of the run context. A “talk to this document” agent then keeps a separate memory for each document:

uc_ai_memory.enable_for_agent(
p_agent_code => 'DOC_AGENT',
p_scope => 'context',
p_context_key => 'document_id'
);

Every run that starts with p_run_context => json_object_t('{"document_id":"7"}') resolves to the store context:DOC_AGENT:document_id:7. A run for document 8 gets its own store, and neither run can read the files of the other.

By default the store belongs to the agent alone. Name a p_store_code to let several agents share one memory per document:

uc_ai_memory.enable_for_agent('DOC_CHAT', p_scope => 'context',
p_context_key => 'document_id', p_store_code => 'DOCS');
uc_ai_memory.enable_for_agent('DOC_SUMMARY', p_scope => 'context',
p_context_key => 'document_id', p_store_code => 'DOCS');

Both agents then resolve to context:DOCS:document_id:7.

The key of a context store always has this shape:

context:<store_code or agent_code>:<context_key>:<context_value>

uc_ai_memory_stores.store_key holds it, so you can read or delete the memory of one document directly. uc_ai_memory.resolve_store_id builds the same key from its parameters.

Two rules apply to the value:

  • The run must supply the key. UC AI checks this before the run starts and raises -20426 when the key is missing. The run does not start, and the error names the key. A run without p_run_context therefore stops instead of reading an empty memory — read Why a missing key fails the run.
  • The value must be at most 200 characters from A-Z, a-z, 0-9, underscore, dot, and hyphen. UC AI refuses any other value, because the store key is built by concatenation.

The MEMORY PROTOCOL names the kinds of things that belong in a memory: decisions, preferences, learnings, and progress. It also tells the model to keep the files small and current. It cannot know which information is important in your domain, so that part is your work.

Put this guidance in the system prompt template of the prompt profile. Describe the information that belongs in a memory, and the information that must never become one:

MEMORY
Record what the next reader cannot get from the tables: why a machine behaves
the way it does, what we already tried, and what the customer prefers.
Keep the current state apart from the long history.
Start every line with the date as YYYY-MM-DD.
When a line stops being true, replace it. Never append a correction below it.
Never record a phone number, a home address, or any other personal data.

The agent picks its own file names from this guidance, and that is enough for a file that only the agent writes and reads. The memory tool has a view command, so the agent reads its own layout at the start of each run.

Name an exact path only for a file that somebody other than the agent touches:

  • a person who seeds a reference note with uc_ai_memory.put_file
  • a second agent that shares the store
  • a query, a report, or an APEX page that reads one file

The path is then an interface, like the name of a table. Name the files also when the model invents a new name for the same subject on each run. A model with no training on this command set needs more structure than a Claude model does.

To add these rules, read the profile row with get_prompt_profile, set system_prompt_template, and pass the row to update_prompt_profile. The model configuration, and with it the memory tool tag, stays as it is.

New rules take effect on the next new session, because an open conversation keeps the system prompt that it started with. Lesson 3 of the shift handover course works through a store that needs a fixed layout: Decide what the desk writes down.

uc_ai_memory_config caps how much an agent can store: max_file_chars (default 100,000), max_files (default 1,000), and an optional max_store_chars. When a write passes a cap, the model gets an error text. This text asks the model to merge or delete old files.

For a cleanup by age, run this block regularly, for example from a scheduler job:

begin
uc_ai_memory.expire_files(p_days => 180); -- drop files untouched for 180 days
commit;
end;
/

The uc_ai_v_memory_files view shows what a store holds. You can also manage the files directly with uc_ai_memory.list_files, get_file, put_file, delete_file, and clear_store_files. Use these procedures to fill the memory of an agent with reference notes.

These procedures take a store id, which uc_ai_memory.resolve_store_id gives you. UC AI creates a store at its first use, so the store of an agent that has not run yet does not exist and resolve_store_id raises -20423. A setup script that clears the memory before the first run must accept that error:

declare
e_no_store exception;
pragma exception_init(e_no_store, -20423);
l_store_id number;
begin
l_store_id := uc_ai_memory.resolve_store_id(
p_scope => 'context',
p_agent_code => 'DOC_AGENT',
p_context_key => 'document_id',
p_context_value => '7'
);
uc_ai_memory.clear_store_files(l_store_id);
commit;
exception
when e_no_store then
null; -- nothing has been written yet
end;
/

The tool also works with a plain generate_text call. First point the session at a named store:

begin
uc_ai_memory.set_store('MY_NOTES'); -- session-level store override
uc_ai.g_enable_tools := true;
uc_ai.g_tool_tags := apex_t_varchar2('memory');
-- ... uc_ai.generate_text(...) — the model can now use the memory tool
uc_ai_memory.clear_store;
end;
/

A standalone call renders no prompt profile, so it gets no MEMORY PROTOCOL block. Add the protocol to your system prompt yourself. uc_ai_memory.get_memory_protocol returns the recommended block.

  • Errors are messages, not exceptions. The Anthropic contract says that the tool returns a text such as “The path … does not exist…”. The model reacts to this text. A memory error never aborts a run.
  • Path safety. Every path must stay under /memories. The tool rejects a traversal attempt with .., with a backslash, or in a URL-encoded form.
  • Transactions. A memory write joins the transaction of the caller. If the run rolls back its work, the memory rolls back with it. The memory therefore stays consistent with the conversation history.
  • Enabling memory inside a conversation. UC AI appends the protocol when the first turn of a conversation renders the system prompt of the profile. Memory therefore takes full effect on the next new session. An open session keeps its original system prompt, but the tool itself works at once.
  • Concurrency. Parallel runs that write the same file serialize on row locks. The last committed write wins.