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_sessionnimmt eine vollständige Konversationsmenge von Einträgen auf. Ein Runner ruft dies für dich auf, wenn Speicher angeschlossen ist.searchist 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:
| Sitzungszustand | Speicher | |
|---|---|---|
| Lebensdauer | Eine Unterhaltung | Über Unterhaltungen hinweg |
| API | adk-session (SessionService, Zustandskarte) | adk-memory (MemoryService) |
| Enthält | Das Live-Transkript + Scratch-Zustand | Dauerhafte Fakten, die es sich später zu merken lohnt |
| Lesen | Immer im Kontext | Bei 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.
}
| Backend | Projektabgrenzung |
|---|---|
InMemoryMemoryService | Ja |
SqliteMemoryService | Ja |
PostgresMemoryService | Ja |
RedisMemoryService | Ja |
MongoMemoryService | Ja |
Neo4jMemoryService | Ja |
GraphMemoryService | Nein — 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 →