對訊息和記憶體使用存留時間
訊息和記憶在建立訊息的對話結束後仍可繼續搜尋。存留時間可讓應用程式在定義的期間之後自動將記錄設為過期,例如滿足合規性需求、移除可能過時的資訊或節省儲存空間。
本指南說明如何設定 Oracle DB 備份訊息和備忘錄的記錄到期。它也說明永久清除工作與每一記錄存留時間覆寫。
Oracle Agent Memory 會在兩層套用存留時間:
- 綱要層次保留預設值來自
MemoryRetentionConfig。新寫入時省略的ttl_days值會使用MemoryRetentionConfig.default_ttl_days。 - 根據記錄的
ttl_days和ttl_anchor值,讓一個訊息或記憶體比綱要預設值更快、更晚或從不同的錨點時間到期。
選擇每個記錄錨點時,請使用 TimeToLiveAnchor.CREATED_AT 來計算 Oracle 儲存資料列時的來源,或改用 TimeToLiveAnchor.TIMESTAMP 來計算記錄的事件時戳。
備註:當記錄應在定義的期間內保持可用狀態,然後自動到期時,請使用「存留時間」。例如,應用程式可能會保留 30 天的支援詳細資料,或保留一週的臨時任務資訊。
適用下列保留規則:
- 在新寫入時省略
ttl_days會使用綱要預設值。 - 傳送的
ttl_days=None會在設定最大值時使用max_ttl_days,或在未設定最大值時儲存非到期的資料列。 - 超過
max_ttl_days的值會固定至該上限,但有警告。 - 除非提供
ttl_days或ttl_anchor,否則更新 API 會保留目前的到期時間。
提示:如需設定套裝程式,請參閱開始使用代理程式記憶體。如果此範例需要本機 Oracle 資料庫,請遵循在本機執行 Oracle AI Database 。
設定綱要層次保留預設值
若要讓 SDK 建立或升級受管理的 Oracle 綱要並同時保存保留原則,請使用 schema_policy=SchemaPolicy.CREATE_IF_NECESSARY 建立 OracleAgentMemory 從屬端。
import oracledb
from oracleagentmemory.apis import TimeToLiveAnchor
from oracleagentmemory.core import (
MemoryExtractionConfig,
MemoryRetentionConfig,
OracleAgentMemory,
SchemaPolicy,
)
from oracleagentmemory.core.embedders.embedder import Embedder
embedder = Embedder(model="YOUR_EMBEDDING_MODEL")
db_pool = oracledb.SessionPool(
user="YOUR DB USER",
password="YOUR DB PASSWORD",
dsn="YOUR DB CONNECT STRING",
)
memory = OracleAgentMemory(
connection=db_pool,
embedder=embedder,
schema_policy=SchemaPolicy.CREATE_IF_NECESSARY,
memory_extraction_config=MemoryExtractionConfig(extract_memories=False),
memory_retention_config=MemoryRetentionConfig(
default_ttl_days=30,
max_ttl_days=90,
),
)
在此組態中:
- 省略
ttl_days的新寫入預設為30天 90天以上的明確ttl_days值會加上警告- 明確的
ttl_days=None會解析為90天,因為已設定max_ttl_days
API 參考: OracleAgentMemory
透過存留時間新增訊息和記憶
在為其他記錄設定不同的 TTL 時,使用繫線寫入來依賴綱要預設 TTL。
from datetime import datetime, timezone
ttl_thread = memory.create_thread(
thread_id="ttl_demo_thread",
user_id="user_123",
)
message_ids = ttl_thread.add_messages(
[
{
"id": "msg-ttl-1",
"role": "user",
"content": (
"I opened ticket 1042 yesterday because the laptop battery failed."
),
"timestamp": "2026-04-01T09:00:00Z",
},
{
"id": "msg-ttl-2",
"role": "assistant",
"content": (
"I will keep ticket 1042 active and send a replacement checklist."
),
"timestamp": "2026-04-01T09:01:00Z",
},
]
)
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
#for instance, 2026-04-01T09:00:00Z
backfilled_memory_id = ttl_thread.add_memory(
"Ticket 1042 battery-failure report was filed on 2026-04-01.",
memory_id="mem-ttl-backfill",
timestamp=timestamp,
ttl_days=90,
ttl_anchor=TimeToLiveAnchor.TIMESTAMP,
)
short_lived_memory_id = ttl_thread.add_memory(
"Replacement checklist should be revisited within one week.",
memory_id="mem-ttl-short",
ttl_days=7,
)
print(message_ids)
print(backfilled_memory_id)
print(short_lived_memory_id)
#['msg-ttl-1', 'msg-ttl-2']
#mem-ttl-backfill
#mem-ttl-short
在本範例中:
add_messages()省略ttl_days,因此兩個訊息都使用綱要預設值30天mem-ttl-backfill會在儲存的事件時戳之後90天到期,因為它使用TimeToLiveAnchor.TIMESTAMPmem-ttl-short會覆寫預設值,並在7天後到期
API 參照:OracleThread
重新整理現有記錄上的存留時間
使用 update_message() 或 update_memory() 重新整理現有到期時間,而不取代儲存的內容。
ttl_thread.update_message("msg-ttl-1", ttl_days=14)
ttl_thread.update_memory(
"mem-ttl-backfill",
ttl_days=60,
ttl_anchor=TimeToLiveAnchor.TIMESTAMP,
)
這些呼叫會在重新計算到期時段時,保留儲存的內容和描述資料。除非您傳送 ttl_days 或 ttl_anchor,否則請記住,更新會保留目前的到期時間。
API 參照:OracleThread
確定受管理的永久清除工作存在
當 Oracle Agent Memory 建立或升級其受管理的 Oracle 綱要時,也會建立每日 DBMS_SCHEDULER 工作,以刪除過期的訊息和記憶體資料列及其擷取區塊。工作會以批次方式永久清除資料列,而不是發出一個大型刪除、在批次之間確認,以及在前面擷取一個參照時戳,讓單一執行使用一個一致的到期截止。
排程器定義也會將 schedule_limit 設為一天,因此可以略過開始太晚的執行,而不是任意延遲執行。工作主體會獨立停止從執行開始時間算起一天後執行新批次。該執行時期限制會在批次之間檢查,因此允許已進行中的批次完成並確認。在執行結束時,工作會寫入簡短的 DBMS_OUTPUT 摘要,包括是否達到執行時期限制,以及依類別刪除的資料列數目。
當 SchemaPolicy.CREATE_IF_NECESSARY 需要建立該工作,但綱要使用者缺少 CREATE JOB 時,設定完成時會出現警告。過期的記錄會保持隱藏,無法讀取和搜尋,但要等到授權的使用者建立工作後,才會實際永久清除這些記錄。
如果使用 table_name_prefix,請在下方的工作和表格名稱套用相同的前置碼。預設的受管理工作名稱為 PURGE_EXPIRED_RECORDS_J。
秘訣:當綱要擁有者沒有 CREATE JOB 時,請使用下列 DBA 選項。
授予排程器工作權限
GRANT CREATE JOB TO app_schema;
-- Run OracleAgentMemory schema setup as app_schema.
REVOKE CREATE JOB FROM app_schema;
手動建立永久清除工作
以受管理綱要擁有者取代 APP_SCHEMA,並在設定表格名稱前置碼時調整物件名稱。下方的 PL/SQL 區塊會刻意壓縮,因此即使前置碼的物件名稱太長,它仍會維持在 Oracle Scheduler 的 job_action 長度限制內:
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'APP_SCHEMA.PURGE_EXPIRED_RECORDS_J',
job_type => 'PLSQL_BLOCK',
job_action => q'[
DECLARE
bs CONSTANT PLS_INTEGER := 1000;
n PLS_INTEGER;
mc PLS_INTEGER := 0;
xc PLS_INTEGER := 0;
mr PLS_INTEGER := 0;
xr PLS_INTEGER := 0;
tl PLS_INTEGER := 0;
rt TIMESTAMP(6) WITH TIME ZONE := SYSTIMESTAMP;
dl TIMESTAMP(6) WITH TIME ZONE := rt + INTERVAL '1 00:00:00' DAY TO SECOND;
BEGIN
LOOP
IF SYSTIMESTAMP >= dl THEN
tl := 1;
EXIT;
END IF;
DELETE FROM APP_SCHEMA.RECORD_CHUNKS c
WHERE c.rowid IN (
SELECT rid
FROM (
SELECT c.rowid AS rid
FROM APP_SCHEMA.RECORD_CHUNKS c
WHERE c.source_record_type = 'message'
AND EXISTS (
SELECT 1
FROM APP_SCHEMA.MESSAGE m
WHERE m.record_id = c.source_id
AND m.expires_at IS NOT NULL
AND m.expires_at <= rt
)
FETCH FIRST bs ROWS ONLY
)
);
n := SQL%ROWCOUNT;
mc := mc + n;
EXIT WHEN n = 0;
COMMIT;
END LOOP;
LOOP
IF SYSTIMESTAMP >= dl THEN
tl := 1;
EXIT;
END IF;
DELETE FROM APP_SCHEMA.RECORD_CHUNKS c
WHERE c.rowid IN (
SELECT rid
FROM (
SELECT c.rowid AS rid
FROM APP_SCHEMA.RECORD_CHUNKS c
WHERE c.source_record_type IN ('fact', 'guideline', 'memory', 'preference')
AND EXISTS (
SELECT 1
FROM APP_SCHEMA.MEMORY m
WHERE m.record_id = c.source_id
AND m.memory_type = c.source_record_type
AND m.expires_at IS NOT NULL
AND m.expires_at <= rt
)
FETCH FIRST bs ROWS ONLY
)
);
n := SQL%ROWCOUNT;
xc := xc + n;
EXIT WHEN n = 0;
COMMIT;
END LOOP;
LOOP
IF SYSTIMESTAMP >= dl THEN
tl := 1;
EXIT;
END IF;
DELETE FROM APP_SCHEMA.MESSAGE
WHERE rowid IN (
SELECT rid
FROM (
SELECT rowid AS rid
FROM APP_SCHEMA.MESSAGE
WHERE expires_at IS NOT NULL
AND expires_at <= rt
FETCH FIRST bs ROWS ONLY
)
);
n := SQL%ROWCOUNT;
mr := mr + n;
EXIT WHEN n = 0;
COMMIT;
END LOOP;
LOOP
IF SYSTIMESTAMP >= dl THEN
tl := 1;
EXIT;
END IF;
DELETE FROM APP_SCHEMA.MEMORY
WHERE rowid IN (
SELECT rid
FROM (
SELECT rowid AS rid
FROM APP_SCHEMA.MEMORY
WHERE expires_at IS NOT NULL
AND expires_at <= rt
FETCH FIRST bs ROWS ONLY
)
);
n := SQL%ROWCOUNT;
xr := xr + n;
EXIT WHEN n = 0;
COMMIT;
END LOOP;
DBMS_OUTPUT.PUT_LINE(
'OracleAgentMemory purge reference_time='
|| TO_CHAR(rt, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZH:TZM')
);
DBMS_OUTPUT.PUT_LINE(
'OracleAgentMemory purge run_deadline='
|| TO_CHAR(dl, 'YYYY-MM-DD"T"HH24:MI:SS.FF3TZH:TZM')
);
DBMS_OUTPUT.PUT_LINE(
'OracleAgentMemory purge runtime_limit_reached='
|| CASE WHEN tl = 1 THEN 'true' ELSE 'false' END
);
DBMS_OUTPUT.PUT_LINE(
'OracleAgentMemory purge deleted message chunk rows='
|| TO_CHAR(mc)
);
DBMS_OUTPUT.PUT_LINE(
'OracleAgentMemory purge deleted fact/guideline/memory/preference chunk rows='
|| TO_CHAR(xc)
);
DBMS_OUTPUT.PUT_LINE(
'OracleAgentMemory purge deleted message rows='
|| TO_CHAR(mr)
);
DBMS_OUTPUT.PUT_LINE(
'OracleAgentMemory purge deleted fact/guideline/memory/preference rows='
|| TO_CHAR(xr)
);
END;
]',
start_date => SYSTIMESTAMP,
repeat_interval => 'FREQ=DAILY;INTERVAL=1',
enabled => FALSE,
auto_drop => FALSE,
comments => 'OracleAgentMemory expired-record purge'
);
DBMS_SCHEDULER.SET_ATTRIBUTE(
name => 'APP_SCHEMA.PURGE_EXPIRED_RECORDS_J',
attribute => 'logging_level',
value => DBMS_SCHEDULER.LOGGING_RUNS
);
DBMS_SCHEDULER.SET_ATTRIBUTE(
name => 'APP_SCHEMA.PURGE_EXPIRED_RECORDS_J',
attribute => 'store_output',
value => TRUE
);
DBMS_SCHEDULER.SET_ATTRIBUTE(
name => 'APP_SCHEMA.PURGE_EXPIRED_RECORDS_J',
attribute => 'schedule_limit',
value => INTERVAL '1 00:00:00' DAY TO SECOND
);
DBMS_SCHEDULER.ENABLE('APP_SCHEMA.PURGE_EXPIRED_RECORDS_J');
END;
/
確認排程器定義並檢查最近的執行歷史記錄非常有用。視需要取代 APP_SCHEMA,並在部署使用時,將已設定的表格名稱前置碼套用至工作名稱。
檢查管理的永久清除工作是否存在並保留預期的檢查設定值:
SELECT owner,
job_name,
enabled,
state,
repeat_interval,
schedule_limit,
logging_level,
store_output,
start_date,
last_start_date,
next_run_date
FROM all_scheduler_jobs
WHERE owner = 'APP_SCHEMA'
AND job_name = 'PURGE_EXPIRED_RECORDS_J';
當您需要確認永久清除執行是否順利完成時,請檢查最近的執行:
SELECT log_date,
status,
run_duration,
output,
additional_info
FROM all_scheduler_job_run_details
WHERE owner = 'APP_SCHEMA'
AND job_name = 'PURGE_EXPIRED_RECORDS_J'
ORDER BY log_date DESC;
如果您想要立即驗證安裝,請手動執行工作一次:
BEGIN
DBMS_SCHEDULER.RUN_JOB(
job_name => 'APP_SCHEMA.PURGE_EXPIRED_RECORDS_J',
use_current_session => TRUE
);
END;
/
結論
在本指南中,我們將探討綱要層次保留預設值、每一記錄的 ttl_days 值和 TimeToLiveAnchor 如何搭配運作、受管理的永久清除工作如何移除過期的資料列,以及如何從 Python 重新整理現有的過期項目。
如需有關依中繼資料篩選記錄的詳細資訊,請參閱使用中繼資料與中繼資料篩選。
完整代碼
下列程式碼會結合本指南中的範例。
#Copyright © 2026 Oracle and/or its affiliates.
#This software is under the Apache License 2.0
#(LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) or Universal Permissive License
#(UPL) 1.0 (LICENSE-UPL or https://oss.oracle.com/licenses/upl), at your option.
#Oracle Agent Memory Code Example - Use Time-to-Live for Messages and Memories
#-----------------------------------------------------------------------------
##Create a retention aware client
from datetime import datetime, timezone
import oracledb
from oracleagentmemory.apis import TimeToLiveAnchor
from oracleagentmemory.core import (
MemoryExtractionConfig,
MemoryRetentionConfig,
OracleAgentMemory,
SchemaPolicy,
)
from oracleagentmemory.core.embedders.embedder import Embedder
embedder = Embedder(model="YOUR_EMBEDDING_MODEL")
db_pool = oracledb.SessionPool(
user="YOUR DB USER",
password="YOUR DB PASSWORD",
dsn="YOUR DB CONNECT STRING",
)
memory = OracleAgentMemory(
connection=db_pool,
embedder=embedder,
schema_policy=SchemaPolicy.CREATE_IF_NECESSARY,
memory_extraction_config=MemoryExtractionConfig(extract_memories=False),
memory_retention_config=MemoryRetentionConfig(
default_ttl_days=30,
max_ttl_days=90,
),
)
##Add messages and memories with time to live
ttl_thread = memory.create_thread(
thread_id="ttl_demo_thread",
user_id="user_123",
)
message_ids = ttl_thread.add_messages(
[
{
"id": "msg-ttl-1",
"role": "user",
"content": (
"I opened ticket 1042 yesterday because the laptop battery failed."
),
"timestamp": "2026-04-01T09:00:00Z",
},
{
"id": "msg-ttl-2",
"role": "assistant",
"content": (
"I will keep ticket 1042 active and send a replacement checklist."
),
"timestamp": "2026-04-01T09:01:00Z",
},
]
)
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
#for instance, 2026-04-01T09:00:00Z
backfilled_memory_id = ttl_thread.add_memory(
"Ticket 1042 battery-failure report was filed on 2026-04-01.",
memory_id="mem-ttl-backfill",
timestamp=timestamp,
ttl_days=90,
ttl_anchor=TimeToLiveAnchor.TIMESTAMP,
)
short_lived_memory_id = ttl_thread.add_memory(
"Replacement checklist should be revisited within one week.",
memory_id="mem-ttl-short",
ttl_days=7,
)
print(message_ids)
print(backfilled_memory_id)
print(short_lived_memory_id)
#['msg-ttl-1', 'msg-ttl-2']
#mem-ttl-backfill
#mem-ttl-short
##Refresh time to live on existing records
ttl_thread.update_message("msg-ttl-1", ttl_days=14)
ttl_thread.update_memory(
"mem-ttl-backfill",
ttl_days=60,
ttl_anchor=TimeToLiveAnchor.TIMESTAMP,
)