Mémoire

Mémoire sémantique à long terme pour les agents IA utilisant adk-memory.

Aperçu

Le système de mémoire fournit un stockage persistant et interrogeable pour les conversations d'agents. Contrairement à l'état de session (qui est éphémère), la mémoire persiste à travers les sessions et permet aux agents de rappeler le contexte pertinent des interactions passées.

Installation

[dependencies]
adk-memory = "0.2.0"

Concepts Clés

MemoryEntry

Un seul enregistrement de mémoire avec son contenu, son auteur et son horodatage :

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(),
};

Trait MemoryService

Le trait principal pour les backends de mémoire :

#[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

Paramètres de requête pour la recherche de mémoire :

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

Implémentation simple en mémoire pour le développement et les tests :

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();

    // Stocker les souvenirs d'une 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?;

    // Rechercher des souvenirs
    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!("Found {} memories", response.memories.len());

    Ok(())
}

Isolation de la Mémoire

Les mémoires sont isolées par :

  • app_name: Différentes applications ont des espaces de mémoire séparés
  • user_id: Les mémoires de chaque utilisateur sont privées
// Mémoires de l'utilisateur A
memory.add_session("app", "user-a", "sess-1", entries_a).await?;

// Mémoires de l'utilisateur B (séparées)
memory.add_session("app", "user-b", "sess-1", entries_b).await?;

// La recherche ne renvoie que les mémoires de l'utilisateur A
let request = SearchRequest {
    query: "topic".to_string(),
    user_id: "user-a".to_string(),
    app_name: "app".to_string(),
};

Comportement de Recherche

Le InMemoryMemoryService utilise la correspondance basée sur les mots :

  1. La requête est tokenisée en mots (minuscules)
  2. Le contenu de chaque mémoire est tokenisé
  3. Les mémoires contenant des mots correspondants sont renvoyées
// Requête : "rust programming"
// Correspond aux mémoires contenant "rust" OU "programming"

Backend de Memory personnalisé

Implémentez MemoryService pour un stockage personnalisé (par exemple, une base de données vectorielle) :

use adk_memory::{MemoryService, MemoryEntry, SearchRequest, SearchResponse};
use adk_core::Result;
use async_trait::async_trait;

pub struct VectorMemoryService {
    // Your vector DB client
}

#[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. Generate embeddings for each entry
        // 2. Store in vector database with metadata
        Ok(())
    }

    async fn search(&self, req: SearchRequest) -> Result<SearchResponse> {
        // 1. Generate embedding for query
        // 2. Perform similarity search
        // 3. Return top-k results
        Ok(SearchResponse { memories: vec![] })
    }
}

Intégration avec les Agents

Memory s'intègre avec 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()?;

Lorsque memory est configurée :

  1. Avant chaque tour, les memories pertinentes sont recherchées
  2. Les memories correspondantes sont injectées dans le contexte
  3. Après chaque session, la conversation est stockée en tant que memories

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      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            │
└─────────────────────────────────────────────────────────────┘

Bonnes Pratiques

PratiqueDescription
Utiliser une base de données vectorielle en productionInMemory est uniquement pour le développement/test
Délimiter par utilisateurToujours inclure l'user_id pour la confidentialité
Limiter les résultatsLimiter les mémoires retournées pour éviter le dépassement de contexte
Nettoyer les anciennes mémoiresMettre en œuvre un TTL ou une archivage pour les données obsolètes
Intégrer stratégiquementStocker des résumés, pas des conversations brutes

Comparaison avec les Sessions

CaractéristiqueÉtat de la SessionMemory
PersistanceDurée de vie de la sessionPermanent
PortéeSession uniqueInter-session
RechercheRecherche par clé-valeurRecherche sémantique
Cas d'utilisationContexte actuelRappel à long terme

Précédent: ← Garde-fous | Suivant: Studio →