Memoria
Memoria semántica a largo plazo para agentes de IA usando adk-memory.
Visión general
El sistema de memoria proporciona almacenamiento persistente y de búsqueda para las conversaciones del agente. A diferencia del estado de sesión (que es efímero), la memoria persiste a través de las sesiones y permite a los agentes recordar contexto relevante de interacciones pasadas.
Instalación
[dependencies]
adk-memory = "0.2.0"
Conceptos clave
MemoryEntry
Un único registro de memoria con content, author y timestamp:
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(),
};
MemoryService Trait
El trait principal para los backends de memoria:
#[async_trait]
pub trait MemoryService: Send + Sync {
/// Store session memories for a user
async fn add_session(
&self,
app_name: &str,
user_id: &str,
session_id: &str,
entries: Vec<MemoryEntry>,
) -> Result<()>;
/// Search memories by query
async fn search(&self, req: SearchRequest) -> Result<SearchResponse>;
}
SearchRequest
Parámetros de consulta para la búsqueda de memoria:
use adk_memory::SearchRequest;
let request = SearchRequest {
query: "user preferences".to_string(),
user_id: "user-123".to_string(),
app_name: "my_app".to_string(),
};
InMemoryMemoryService
Implementación sencilla en memoria para desarrollo y pruebas:
use adk_memory::{InMemoryMemoryService, MemoryService, MemoryEntry, SearchRequest};
use adk_core::Content;
use chrono::Utc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let memory = InMemoryMemoryService::new();
// Store memories from a session
let entries = vec![
MemoryEntry {
content: Content::new("user").with_text("I like Rust programming"),
author: "user".to_string(),
timestamp: Utc::now(),
},
MemoryEntry {
content: Content::new("assistant").with_text("Rust is great for systems programming"),
author: "assistant".to_string(),
timestamp: Utc::now(),
},
];
memory.add_session("my_app", "user-123", "session-1", entries).await?;
// Search memories
let request = SearchRequest {
query: "Rust".to_string(),
user_id: "user-123".to_string(),
app_name: "my_app".to_string(),
};
let response = memory.search(request).await?;
println!("Se encontraron {} memorias", response.memories.len());
Ok(())
}
Aislamiento de la memoria
Las memorias se aíslan por:
- app_name: Diferentes aplicaciones tienen espacios de memoria separados
- user_id: Las memorias de cada usuario son privadas
// Memorias del usuario A
memory.add_session("app", "user-a", "sess-1", entries_a).await?;
// Memorias del usuario B (separadas)
memory.add_session("app", "user-b", "sess-1", entries_b).await?;
// La búsqueda solo devuelve las memorias del usuario A
let request = SearchRequest {
query: "topic".to_string(),
user_id: "user-a".to_string(),
app_name: "app".to_string(),
};
Comportamiento de búsqueda
El InMemoryMemoryService utiliza la coincidencia basada en palabras:
- La consulta se tokeniza en palabras (minúsculas)
- El content de cada memoria se tokeniza
- Se devuelven las memorias con cualquier palabra coincidente
// Consulta: "rust programming"
// Coincide con memorias que contienen "rust" O "programming"
Backend de Memoria Personalizado
Implemente MemoryService para almacenamiento personalizado (p. ej., base de datos vectorial):
use adk_memory::{MemoryService, MemoryEntry, SearchRequest, SearchResponse};
use adk_core::Result;
use async_trait::async_trait;
pub struct VectorMemoryService {
// Su cliente de base de datos vectorial
}
#[async_trait]
impl MemoryService for VectorMemoryService {
async fn add_session(
&self,
app_name: &str,
user_id: &str,
session_id: &str,
entries: Vec<MemoryEntry>,
) -> Result<()> {
// 1. Genere incrustaciones para cada entrada
// 2. Almacene en la base de datos vectorial con metadatos
Ok(())
}
async fn search(&self, req: SearchRequest) -> Result<SearchResponse> {
// 1. Genere la incrustación para la consulta
// 2. Realice la búsqueda de similitud
// 3. Devuelva los resultados top-k
Ok(SearchResponse { memories: vec![] })
}
}
Integración con Agentes
La memoria se integra con LlmAgentBuilder:
use adk_agent::LlmAgentBuilder;
use adk_memory::InMemoryMemoryService;
use std::sync::Arc;
let memory = Arc::new(InMemoryMemoryService::new());
let agent = LlmAgentBuilder::new("assistant")
.model(model)
.instruction("You are a helpful assistant with memory.")
.memory(memory)
.build()?;
Cuando la memoria está configurada:
- Antes de cada turno, se buscan las memorias relevantes
- Las memorias coincidentes se inyectan en el contexto
- Después de cada sesión, la conversación se almacena como memorias
Arquitectura
┌─────────────────────────────────────────────────────────────┐
│ Agent Request │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Memory Search │
│ │
│ SearchRequest { query, user_id, app_name } │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MemoryService │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │ │
│ │ │ InMemory │ │ Vector DB │ │ Custom │ │ │
│ │ │ (dev/test) │ │ (Qdrant) │ │ Backend │ │ │
│ │ └─────────────┘ └─────────────┘ └────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ SearchResponse { memories: Vec<MemoryEntry> } │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Context Injection │
│ │
│ Relevant memories added to agent context │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Agent Execution │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Memory Storage │
│ │
│ Session conversation stored for future recall │
└─────────────────────────────────────────────────────────────┘
Mejores Prácticas
| Práctica | Descripción |
|---|---|
| Usar DB vectorial en producción | InMemory es solo para desarrollo/pruebas |
| Limitar por usuario | Siempre incluir user_id por privacidad |
| Limitar resultados | Limitar las memorias devueltas para evitar el desbordamiento de contexto |
| Limpiar memorias antiguas | Implementar TTL o archivado para datos obsoletos |
| Integrar estratégicamente | Almacenar resúmenes, no conversaciones en bruto |
Comparación con Sesiones
| Característica | Estado de Sesión | Memoria |
|---|---|---|
| Persistencia | Duración de la Session | Permanente |
| Alcance | Sesión única | Entre sesiones |
| Búsqueda | Consulta clave-valor | Búsqueda semántica |
| Caso de uso | Contexto actual | Recuperación a largo plazo |
Anterior: ← Guardrails | Siguiente: Studio →