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_session ingiere las entradas de una conversación completada. Un Runner llama a esto por ti cuando la memoria está adjunta.
  • search es 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ónMemoria
Ciclo de vidaUna conversaciónA lo largo de las conversaciones
APIadk-session (SessionService, mapa de estado)adk-memory (MemoryService)
ContieneLa transcripción en vivo + estado temporalHechos duraderos que vale la pena recordar más tarde
LeerSiempre en contextoBajo 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.
}
BackendAlcance del proyecto
InMemoryMemoryService
SqliteMemoryService
PostgresMemoryService
RedisMemoryService
MongoMemoryService
Neo4jMemoryService
GraphMemoryServiceNo — 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 →