Concepts de mémoire
La partie du magasin sémantique de la mémoire est construite à partir de quelques petits types. Apprenez ces quatre-là, et le reste de la section suit.
MemoryEntry
Un seul enregistrement de mémoire : un contenu, son auteur, et quand il a été créé.
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(),
};
Les entrées sont l’unité de stockage et l’unité de rappel — search renvoie les
entrées les plus pertinentes pour une requête.
Le trait MemoryService
Chaque backend implémente un trait. Deux méthodes sont requises ; le reste a des implémentations par défaut qu’un backend peut remplacer :
#[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_sessioningère la totalité des entrées d’une conversation terminée. Un Runner l’appelle pour vous lorsque la mémoire est attachée.searchest le rappel. Les backends interprètent la requête différemment — mots-clés, similarité d’embedding, ou notation de jetons de graphe — mais le contrat est le même.
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>
État de session vs mémoire
Ce sont des outils différents — ne les confondez pas :
| État de session | Mémoire | |
|---|---|---|
| Durée de vie | Une conversation | À travers les conversations |
| API | adk-session (SessionService, carte d’état) | adk-memory (MemoryService) |
| Contient | La transcription en direct + l'état temporaire | Des faits durables dignes d'être rappelés plus tard |
| Lecture | Toujours dans le contexte | À la demande, via search_memory |
Un flux typique : la conversation vit dans l’état de session ; lorsqu’elle se termine (ou à chaque tour), les éléments saillants sont écrits en mémoire ; la session suivante recherche la mémoire pour reconstituer le contexte. Voir Sessions & State.
Isolation : application, utilisateur et projet
La mémoire est toujours indexée par (app_name, user_id), donc les utilisateurs ne voient jamais les
mémoires des autres. Vous pouvez définir un troisième niveau — projet — au sein d’un utilisateur :
- Entrées globales (
project_id = None) — visibles dans tous les contextes. - Entrées de projet (
project_id = Some(id)) — visibles uniquement dans ce projet. - Recherche de projet renvoie les entrées globales + les entrées de projet correspondantes ; la recherche globale ne renvoie que les entrées globales.
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");
Utilisez les projets pour éviter, par exemple, qu’un assistant professionnel et un assistant personnel d’un utilisateur partagent la mémoire, tout en partageant les faits réellement globaux.
Tous les backends n’implémentent pas la portée par projet
L’isolation par projet est une क्षमता du backend, pas quelque chose que le trait peut garantir. Vérifiez avant de vous y fier :
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 | Délimitation du projet |
|---|---|
InMemoryMemoryService | Oui |
SqliteMemoryService | Oui |
PostgresMemoryService | Oui |
RedisMemoryService | Oui |
MongoMemoryService | Oui |
Neo4jMemoryService | Oui |
GraphMemoryService | Non — les méthodes du projet renvoient une erreur |
Un backend sans prise en charge des projets échoue l’appel plutôt que d’écrire ou de supprimer silencieusement à l’échelle globale. Étendre la portée d’une écriture rendrait des données prévues pour un projet visibles par tout ce qui se trouve sous la même application et le même utilisateur, et étendre une suppression retirerait des entrées en dehors du projet nommé — aucun de ces cas ne peut être détecté par l’appelant à partir de la valeur de retour. MemoryServiceAdapter::supports_project_scoping
indique ce que son backend prend en charge.
Effacement RGPD
delete_user(app, user) supprime tous les souvenirs d’un utilisateur (entrées et
embeddings) sur l’ensemble des projets — le primitive du droit à l’effacement. Les backends qui
stockent de manière persistante l’implémentent ; appelez-le depuis votre chemin de suppression de compte.
memory.delete_user("support", "alice").await?;
Suivant : Backends →