Memory-Konzepte

Die semantische Speicher-Seite des Speichers besteht aus einer Handvoll kleiner Typen. Lerne diese vier kennen, und der Rest des Abschnitts erschließt sich von selbst.

MemoryEntry

Ein einzelner Speichereintrag: ein Inhalt, wer ihn verfasst hat und wann.

use adk_memory::MemoryEntry;
use adk_core::Content;
use chrono::Utc;

let entry = MemoryEntry {
    content: Content::new("user").with_text("I prefer dark mode"),
    author: "user".to_string(),
    timestamp: Utc::now(),
};

Einträge sind die Speicher- und Abrufeinheit — search gibt die Einträge zurück, die für eine Abfrage am relevantesten sind.

Das MemoryService-Trait

Jedes Backend implementiert ein Trait. Zwei Methoden sind erforderlich; der Rest hat Standardimplementierungen, die ein Backend überschreiben kann:

#[async_trait]
pub trait MemoryService: Send + Sync {
    // Required
    async fn add_session(&self, app: &str, user: &str, session: &str,
                         entries: Vec<MemoryEntry>) -> Result<()>;
    async fn search(&self, req: SearchRequest) -> Result<SearchResponse>;

    // Optional (default: "not implemented")
    async fn delete_user(&self, app: &str, user: &str) -> Result<()>;          // GDPR
    async fn delete_session(&self, app: &str, user: &str, session: &str) -> Result<()>;
    async fn add_entry(&self, app: &str, user: &str, entry: MemoryEntry) -> Result<()>;
    async fn delete_entries(&self, app: &str, user: &str, query: &str) -> Result<u64>;
    // …plus a health check
}
  • add_session nimmt eine vollständige Konversationsmenge von Einträgen auf. Ein Runner ruft dies für dich auf, wenn Speicher angeschlossen ist.
  • search ist der Abruf. Backends interpretieren die Abfrage unterschiedlich — Schlüsselwort, Embedding-Ähnlichkeit oder Graph-Token-Bewertung — aber der Vertrag ist derselbe.

SearchRequest / SearchResponse

use adk_memory::SearchRequest;

let req = SearchRequest {
    app_name: "support".into(),
    user_id:  "alice".into(),
    query:    "contact preference".into(),
    ..Default::default()      // top_k, project scoping, etc.
};

let resp = memory.search(req).await?;   // resp.entries: Vec<MemoryEntry>

Sitzungszustand vs. Speicher

Das sind unterschiedliche Werkzeuge — vermische sie nicht:

SitzungszustandSpeicher
LebensdauerEine UnterhaltungÜber Unterhaltungen hinweg
APIadk-session (SessionService, Zustandskarte)adk-memory (MemoryService)
EnthältDas Live-Transkript + Scratch-ZustandDauerhafte Fakten, die es sich später zu merken lohnt
LesenImmer im KontextBei Bedarf, über search_memory

Ein typischer Ablauf: Die Konversation lebt im Sitzungszustand; wenn sie endet (oder mit jedem Turn), werden relevante Teile in den Speicher geschrieben; die nächste Sitzung durchsucht den Speicher, um den Kontext wiederherzustellen. Siehe Sessions & State.

Isolation: App, Benutzer und Projekt

Speicher ist immer nach (app_name, user_id) gruppiert, sodass Benutzer nie die Speicher der anderen sehen. Sie können eine dritte Ebene — Projekt — innerhalb eines Benutzers abgrenzen:

  • Globale Einträge (project_id = None) — in jedem Kontext sichtbar.
  • Projekteinträge (project_id = Some(id)) — nur innerhalb dieses Projekts sichtbar.
  • Projektsuche gibt globale + passende Projekteinträge zurück; globale Suche gibt nur globale Einträge zurück.
use adk_memory::{MemoryServiceAdapter, InMemoryMemoryService};
use std::sync::Arc;

let service = Arc::new(InMemoryMemoryService::new());

// store within a project
service.add_session_to_project("app", "user", "sess", "acme-project", entries).await?;

// an adapter scoped to that project
let adapter = MemoryServiceAdapter::new(service, "app", "user")
    .with_project_id("acme-project");

Verwenden Sie Projekte, um zum Beispiel die Arbeits- und Privat-Assistenten eines Benutzers davon abzuhalten, Speicher zu teilen, während wirklich globale Fakten weiterhin gemeinsam genutzt werden.

Nicht jedes Backend implementiert Projektabgrenzung

Die Projektisolation ist eine Backend-Funktionalität, nichts, was das Trait garantieren kann. Fragen Sie nach, bevor Sie sich darauf verlassen:

if service.supports_project_scoping() {
    service.add_entry_to_project("app", "user", "acme-project", entry).await?;
} else {
    // Decide explicitly: refuse, or store globally on purpose.
}
BackendProjektabgrenzung
InMemoryMemoryServiceJa
SqliteMemoryServiceJa
PostgresMemoryServiceJa
RedisMemoryServiceJa
MongoMemoryServiceJa
Neo4jMemoryServiceJa
GraphMemoryServiceNein — Projektmethoden geben einen Fehler zurück

Ein Backend ohne Projektunterstützung schlägt den Aufruf fehl, anstatt stillschweigend global zu schreiben oder zu löschen. Das Erweitern des Gültigkeitsbereichs eines Schreibvorgangs würde Daten, die für ein Projekt bestimmt sind, für alles unter derselben App und demselben Benutzer sichtbar machen, und das Erweitern eines Löschvorgangs würde Einträge außerhalb des benannten Projekts entfernen — beides ist etwas, das ein Aufrufer nicht aus dem Rückgabewert erkennen könnte. MemoryServiceAdapter::supports_project_scoping gibt an, was sein Backend unterstützt.

DSGVO-Löschung

delete_user(app, user) entfernt alle Erinnerungen eines Benutzers (Einträge und Embeddings) über alle Projekte hinweg — die Primitive für das Recht auf Löschung. Backends, die dauerhaft speichern, implementieren sie; rufe sie in deinem Konto-Löschpfad auf.

memory.delete_user("support", "alice").await?;

Weiter: Backends →