Conceptos de memoria
La parte del almacenamiento semántico de la memoria se construye a partir de unos pocos tipos pequeños. Aprende estos cuatro y el resto de la sección seguirá.
MemoryEntry
Un único registro de memoria: algo de contenido, quién lo redactó y cuándo.
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(),
};
Las entradas son la unidad de almacenamiento y la unidad de recuperación — search devuelve las
entradas más relevantes para una consulta.
El trait MemoryService
Cada backend implementa un trait. Se requieren dos métodos; el resto tiene implementaciones por defecto que un backend puede sobrescribir:
#[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_sessioningiere las entradas de una conversación completada. Un Runner llama a esto por ti cuando la memoria está adjunta.searches la recuperación. Los backends interpretan la consulta de forma distinta — coincidencia por palabras clave, similitud de embedding o puntuación de tokens en el grafo — pero el contrato es el mismo.
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>
Estado de sesión vs. memoria
Son herramientas distintas — no las confundas:
| Estado de sesión | Memoria | |
|---|---|---|
| Ciclo de vida | Una conversación | A lo largo de las conversaciones |
| API | adk-session (SessionService, mapa de estado) | adk-memory (MemoryService) |
| Contiene | La transcripción en vivo + estado temporal | Hechos duraderos que vale la pena recordar más tarde |
| Leer | Siempre en contexto | Bajo demanda, mediante search_memory |
Un flujo típico: la conversación vive en el estado de sesión; cuando termina (o en cada turno), las partes relevantes se escriben en la memoria; la siguiente sesión busca en la memoria para rehidratar el contexto. Consulta Sessions & State.
Aislamiento: aplicación, usuario y proyecto
La memoria siempre se indexa por (app_name, user_id), así que los usuarios nunca ven las
memorias de los demás. Puedes delimitar un tercer nivel — proyecto — dentro de un usuario:
- Entradas globales (
project_id = None) — visibles en todos los contextos. - Entradas del proyecto (
project_id = Some(id)) — visibles solo dentro de ese proyecto. - La búsqueda en el proyecto devuelve las entradas globales + las entradas del proyecto que coinciden; la búsqueda global devuelve solo las entradas 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");
Usa proyectos para evitar, por ejemplo, que los asistentes de trabajo y personales de un usuario compartan memoria, al tiempo que siguen compartiendo hechos realmente globales.
No todos los backends implementan el alcance por proyecto
El aislamiento por proyecto es una capacidad del backend, no algo que el trait pueda garantizar. Pregúntalo antes de depender de ello:
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 | Alcance del proyecto |
|---|---|
InMemoryMemoryService | Sí |
SqliteMemoryService | Sí |
PostgresMemoryService | Sí |
RedisMemoryService | Sí |
MongoMemoryService | Sí |
Neo4jMemoryService | Sí |
GraphMemoryService | No — los métodos del proyecto devuelven un error |
Un backend sin soporte de proyecto falla la llamada en lugar de escribir o eliminar silenciosamente de forma global. Ampliar el alcance de una escritura haría visibles los datos destinados a un proyecto para todo lo que esté bajo la misma app y usuario, y ampliar una eliminación borraría entradas fuera del proyecto nombrado — ninguna de las dos cosas es algo que una llamada pudiera detectar a partir del valor de retorno. MemoryServiceAdapter::supports_project_scoping
informa lo que admite su backend.
Eliminación GDPR
delete_user(app, user) elimina todos los recuerdos de un usuario (entradas y
embeddings) en todos los proyectos — el primitivo del derecho al borrado. Los backends que
almacenan de forma duradera lo implementan; llámalo desde tu ruta de eliminación de cuenta.
memory.delete_user("support", "alice").await?;
Siguiente: Backends →