Live-Zeit für Nachrichten und Erinnerungen verwenden

Nachrichten und Erinnerungen können nach dem Ende der Unterhaltung, die sie erstellt hat, durchsucht werden. Mit Time-to-Live können Anwendungen Datensätze nach einem definierten Zeitraum automatisch ablaufen lassen, z.B. um Compliance-Anforderungen zu erfüllen, Informationen zu entfernen, die wahrscheinlich veraltet sind, oder Speicherplatz zu sparen.

In dieser Anleitung wird gezeigt, wie Sie den Ablauf von Datensätzen für von Oracle DB gesicherte Nachrichten und Speicher konfigurieren. Außerdem werden Löschjobs und Überschreibungen für die Gültigkeitsdauer pro Datensatz erläutert.

Oracle Agent Memory wendet die Gültigkeitsdauer auf zwei Ebenen an:

Wenn Sie einen Anker pro Datensatz auswählen, verwenden Sie TimeToLiveAnchor.CREATED_AT, um zu zählen, wann Oracle die Zeile speichert, oder TimeToLiveAnchor.TIMESTAMP, um stattdessen den Ereigniszeitstempel des Datensatzes zu zählen.

Hinweis: Verwenden Sie die Gültigkeitsdauer, wenn Datensätze für eine definierte Periode verfügbar bleiben sollen und dann automatisch ablaufen. Beispiel: Eine Anwendung behält Supportdetails für 30 Tage oder temporäre Aufgabeninformationen für eine Woche bei.

Es gelten die folgenden Aufbewahrungsregeln:

Hinweis: Informationen zum Package-Setup finden Sie unter Erste Schritte mit Agent-Speicher. Wenn Sie für dieses Beispiel eine lokale Oracle-Datenbank benötigen, folgen Sie Oracle AI Database lokal ausführen.

Aufbewahrungsstandards auf Schemaebene konfigurieren

Erstellen Sie einen OracleAgentMemory-Client mit schema_policy=SchemaPolicy.CREATE_IF_NECESSARY, wenn das SDK das verwaltete Oracle-Schema erstellen oder upgraden soll, und persistieren Sie eine Aufbewahrungs-Policy daneben.

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,
    ),
)

In dieser Konfiguration:

API-Referenz: OracleAgentMemory

Nachrichten und Erinnerungen mit Time-to-Live hinzufügen

Verwenden Sie Thread-Schreibvorgänge, um sich auf die Schema-Standard-TTL für einige Datensätze zu verlassen, während Sie eine andere TTL für andere festlegen.

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

In diesem Beispiel:

API-Referenz: OracleThread

Gültigkeitsdauer für vorhandene Datensätze aktualisieren

Verwenden Sie update_message() oder update_memory(), um einen vorhandenen Ablauf zu aktualisieren, ohne den gespeicherten Inhalt zu ersetzen.

ttl_thread.update_message("msg-ttl-1", ttl_days=14)

ttl_thread.update_memory(
    "mem-ttl-backfill",
    ttl_days=60,
    ttl_anchor=TimeToLiveAnchor.TIMESTAMP,
)

Bei diesen Aufrufen werden der gespeicherte Inhalt und die Metadaten beibehalten, während das Ablauffenster neu berechnet wird. Beachten Sie, dass Updates den aktuellen Ablauf beibehalten, es sei denn, Sie übergeben ttl_days oder ttl_anchor.

API-Referenz: OracleThread

Stellen Sie sicher, dass der verwaltete Löschjob vorhanden ist

Wenn der Oracle Agent-Speicher sein verwaltetes Oracle-Schema erstellt oder aktualisiert, erstellt er auch einen täglichen DBMS_SCHEDULER-Job, der abgelaufene Nachrichten- und Speicherzeilen und deren Abruf-Chunks löscht. Der Job löscht Zeilen in Batches, anstatt einen großen Löschvorgang auszugeben, schreibt zwischen Batches fest und erfasst einen Referenzzeitstempel im Voraus, sodass eine einzelne Ausführung einen konsistenten Ablaufgrenzwert verwendet.

Die Scheduler-Definition setzt schedule_limit auch auf einen Tag, sodass eine Ausführung, die zu spät beginnt, übersprungen werden kann, anstatt willkürlich zu spät auszuführen. Der Jobbody stoppt die Aufnahme neuer Batches nach einem Tag nach der Startzeit der Ausführung. Dieses Laufzeitlimit wird zwischen Batches geprüft, sodass ein bereits in Bearbeitung befindlicher Batch beendet und festgeschrieben werden kann. Am Ende einer Ausführung schreibt der Job eine kurze DBMS_OUTPUT-Übersicht, die enthält, ob der Laufzeitgrenzwert erreicht wurde und wie viele Zeilen nach Kategorie gelöscht wurden.

Wenn SchemaPolicy.CREATE_IF_NECESSARY diesen Job erstellen muss, dem Schemabenutzer jedoch CREATE JOB fehlt, wird das Setup mit einer Warnung abgeschlossen. Abgelaufene Datensätze bleiben vor Lese- und Suchvorgängen verborgen, werden jedoch erst physisch gelöscht, wenn ein berechtigter Benutzer den Job erstellt.

Wenn Sie table_name_prefix verwenden, wenden Sie dasselbe Präfix auf die folgenden Job- und Tabellennamen an. Der standardmäßige verwaltete Jobname lautet PURGE_EXPIRED_RECORDS_J.

Tipp: Verwenden Sie die folgenden DBA-Optionen, wenn der Schemaeigentümer nicht über CREATE JOB verfügt.

Scheduler-Jobberechtigungen erteilen

GRANT CREATE JOB TO app_schema;
-- Run OracleAgentMemory schema setup as app_schema.
REVOKE CREATE JOB FROM app_schema;

Löschjob manuell erstellen

Ersetzen Sie APP_SCHEMA durch den Eigentümer des verwalteten Schemas, und passen Sie Objektnamen an, wenn ein Tabellennamenspräfix konfiguriert ist. Der unten stehende PL/SQL-Block ist absichtlich kompakt, sodass er innerhalb des job_action-Längenlimits von Oracle Scheduler bleibt, selbst wenn vorangestellte Objektnamen lang sind:

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;
/

Es ist nützlich, die Scheduler-Definition zu bestätigen und die aktuelle Ausführungshistorie zu prüfen. Ersetzen Sie bei Bedarf APP_SCHEMA, und wenden Sie das konfigurierte Tabellennamenspräfix auf den Jobnamen an, wenn Ihr Deployment eines verwendet.

Prüfen Sie, ob der verwaltete Löschjob vorhanden ist, und behalten Sie die erwarteten Prüfungseinstellungen bei:

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';

Prüfen Sie die letzten Ausführungen, wenn Sie bestätigen müssen, dass Löschausführungen ausgeführt werden und erfolgreich abgeschlossen werden:

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;

Führen Sie den Job einmal manuell aus, wenn Sie die Installation sofort validieren möchten:

BEGIN
  DBMS_SCHEDULER.RUN_JOB(
    job_name => 'APP_SCHEMA.PURGE_EXPIRED_RECORDS_J',
    use_current_session => TRUE
  );
END;
/

Schlussfolgerung

In dieser Dokumentation haben wir gelernt, wie Aufbewahrungsstandards auf Schemaebene, ttl_days-Werte pro Datensatz und TimeToLiveAnchor zusammenarbeiten, wie der verwaltete Löschjob abgelaufene Zeilen entfernt und wie vorhandene Abläufe aus Python aktualisiert werden.

Weitere Informationen zum Filtern von Datensätzen nach Metadaten finden Sie unter Metadaten- und Metadatenfilter verwenden.

Vollständiger Code

Der folgende Code kombiniert die Beispiele in dieser Dokumentation.

#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,
)