Sessions

Les Sessions dans adk-rust fournissent une gestion du contexte de conversation, permettant aux Agents de maintenir un état à travers de multiples interactions. Les Sessions stockent l'historique de conversation (événements) et des données d'état arbitraires qui persistent tout au long d'une conversation.

Aperçu

Une session représente une conversation unique entre un utilisateur et un Agent. Chaque session :

  • Possède un identifiant unique
  • Appartient à une application (app_name) et un utilisateur (user_id)
  • Contient une liste d'événements (historique de conversation)
  • Maintient des données d'état (paires clé-valeur)
  • Enregistre la dernière heure de mise à jour

Trait Session

Le trait Session définit l'interface pour les objets de session :

use adk_session::{Events, State};
use chrono::{DateTime, Utc};

pub trait Session: Send + Sync {
    /// Unique session identifier
    fn id(&self) -> &str;
    
    /// Application name this session belongs to
    fn app_name(&self) -> &str;
    
    /// User identifier
    fn user_id(&self) -> &str;
    
    /// Access session state
    fn state(&self) -> &dyn State;
    
    /// Access conversation events
    fn events(&self) -> &dyn Events;
    
    /// Last time the session was updated
    fn last_update_time(&self) -> DateTime<Utc>;
}

Trait SessionService

Le trait SessionService définit les opérations pour la gestion des sessions :

use adk_session::{CreateRequest, GetRequest, ListRequest, DeleteRequest, Event, Session};
use adk_core::Result;
use async_trait::async_trait;

#[async_trait]
pub trait SessionService: Send + Sync {
    /// Create a new session
    async fn create(&self, req: CreateRequest) -> Result<Box<dyn Session>>;
    
    /// Retrieve an existing session
    async fn get(&self, req: GetRequest) -> Result<Box<dyn Session>>;
    
    /// List all sessions for an app/user
    async fn list(&self, req: ListRequest) -> Result<Vec<Box<dyn Session>>>;
    
    /// Delete a session
    async fn delete(&self, req: DeleteRequest) -> Result<()>;
    
    /// Append an event to a session
    async fn append_event(&self, session_id: &str, event: Event) -> Result<()>;
}

Types de Requêtes

CreateRequest

use adk_session::CreateRequest;
use std::collections::HashMap;

let request = CreateRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: None,  // Auto-generate UUID if None
    state: HashMap::new(),  // Initial state
};

GetRequest

use adk_session::GetRequest;

let request = GetRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_abc".to_string(),
    num_recent_events: Some(10),  // Limit events returned
    after: None,  // Filter events after timestamp
};

ListRequest

use adk_session::ListRequest;

let request = ListRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
};

DeleteRequest

use adk_session::DeleteRequest;

let request = DeleteRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_abc".to_string(),
};

Implémentations de SessionService

ADK-Rust propose deux implémentations de SessionService :

InMemorySessionService

Stocke les sessions en mémoire. Idéal pour le développement, les tests et les déploiements à instance unique.

use adk_session::{InMemorySessionService, SessionService, CreateRequest};
use std::collections::HashMap;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Create the service
    let session_service = InMemorySessionService::new();
    
    // Create a session
    let session = session_service.create(CreateRequest {
        app_name: "my_app".to_string(),
        user_id: "user_123".to_string(),
        session_id: None,
        state: HashMap::new(),
    }).await?;
    
    println!("Session ID: {}", session.id());
    println!("App: {}", session.app_name());
    println!("User: {}", session.user_id());
    
    Ok(())
}

DatabaseSessionService

Stocke les sessions dans une base de données SQLite. Convient aux déploiements en production nécessitant de la persistance.

use adk_session::{DatabaseSessionService, SessionService, CreateRequest};
use std::collections::HashMap;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Connect to database
    let session_service = DatabaseSessionService::new("sqlite:sessions.db").await?;
    
    // Run migrations to create tables
    session_service.migrate().await?;
    
    // Create a session
    let session = session_service.create(CreateRequest {
        app_name: "my_app".to_string(),
        user_id: "user_123".to_string(),
        session_id: None,
        state: HashMap::new(),
    }).await?;
    
    println!("Session persisted: {}", session.id());
    
    Ok(())
}

Note: Le DatabaseSessionService nécessite la feature flag database :

adk-session = { version = "0.2", features = ["database"] }

Cycle de vie d'une Session

1. Création

Les sessions sont créées avec une CreateRequest. Si aucun session_id n'est fourni, un UUID est généré automatiquement.

use adk_session::{InMemorySessionService, SessionService, CreateRequest};
use std::collections::HashMap;

let service = InMemorySessionService::new();

// Create with auto-generated ID
let session = service.create(CreateRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: None,
    state: HashMap::new(),
}).await?;

// Create with specific ID
let session = service.create(CreateRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: Some("my-custom-id".to_string()),
    state: HashMap::new(),
}).await?;

2. Récupération

Récupérer une session par ses identifiants :

use adk_session::GetRequest;

let session = service.get(GetRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_abc".to_string(),
    num_recent_events: None,
    after: None,
}).await?;

println!("Retrieved session: {}", session.id());
println!("Events: {}", session.events().len());

3. Ajout d'Événements

Les événements sont ajoutés aux sessions au fur et à mesure de la conversation. Ceci est généralement géré par le Runner, mais peut être fait manuellement :

use adk_session::Event;

let event = Event::new("invocation_123");
service.append_event(session.id(), event).await?;

4. Liste

Lister toutes les sessions pour un utilisateur :

use adk_session::ListRequest;

let sessions = service.list(ListRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
}).await?;

for session in sessions {
    println!("Session: {} (updated: {})", 
        session.id(), 
        session.last_update_time()
    );
}

5. Suppression

Supprimer une session lorsqu'elle n'est plus nécessaire :

use adk_session::DeleteRequest;

service.delete(DeleteRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_abc".to_string(),
}).await?;

Utiliser les Sessions avec Runner

Les Sessions sont généralement gérées par le Runner lors de l'exécution des agents. Le Runner :

  1. Crée ou récupère une session
  2. Transmet le contexte de la session à l'Agent
  3. Ajoute des événements au fur et à mesure de la conversation
  4. Met à jour l'état de la session en fonction des actions de l'Agent
use adk_rust::prelude::*;
use adk_runner::{Runner, RunnerConfig};
use adk_session::InMemorySessionService;
use std::sync::Arc;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    dotenvy::dotenv().ok();
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.0-flash")?);

    let agent = LlmAgentBuilder::new("assistant")
        .model(model)
        .instruction("You are a helpful assistant.")
        .build()?;

    let session_service = Arc::new(InMemorySessionService::new());

    // Create runner with session service
    let runner = Runner::new(RunnerConfig {
        app_name: "my_app".to_string(),
        agent: Arc::new(agent),
        session_service,
        artifact_service: None,
        memory_service: None,
        run_config: None,
    })?;

    // Run with user and session IDs
    let user_content = Content::new("user").with_text("Hello!");
    let stream = runner.run(
        "user_123".to_string(),
        "session_abc".to_string(),
        user_content,
    ).await?;

    Ok(())
}

Événements

Le trait Events donne accès à l'historique de la conversation :

pub trait Events: Send + Sync {
    /// Récupère tous les événements
    fn all(&self) -> Vec<Event>;
    
    /// Récupère le nombre d'événements
    fn len(&self) -> usize;
    
    /// Récupère l'événement à l'index donné
    fn at(&self, index: usize) -> Option<&Event>;
    
    /// Vérifie si la collection est vide
    fn is_empty(&self) -> bool;
}

Accéder aux événements d'une session :

let events = session.events();
println!("Total events: {}", events.len());

for event in events.all() {
    println!("Event {} by {} at {}", 
        event.id, 
        event.author, 
        event.timestamp
    );
}

Exemple Complet

use adk_session::{
    InMemorySessionService, SessionService, 
    CreateRequest, GetRequest, ListRequest, DeleteRequest,
    Event, KEY_PREFIX_USER,
};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let service = InMemorySessionService::new();
    
    // Créer une session avec un état initial
    let mut initial_state = HashMap::new();
    initial_state.insert(format!("{}name", KEY_PREFIX_USER), json!("Alice"));
    initial_state.insert("topic".to_string(), json!("Getting started"));
    
    let session = service.create(CreateRequest {
        app_name: "demo".to_string(),
        user_id: "alice".to_string(),
        session_id: None,
        state: initial_state,
    }).await?;
    
    println!("Created session: {}", session.id());
    
    // Vérifier l'état
    let state = session.state();
    println!("User name: {:?}", state.get("user:name"));
    println!("Topic: {:?}", state.get("topic"));
    
    // Ajouter un événement
    let event = Event::new("inv_001");
    service.append_event(session.id(), event).await?;
    
    // Récupérer la session avec les événements
    let session = service.get(GetRequest {
        app_name: "demo".to_string(),
        user_id: "alice".to_string(),
        session_id: session.id().to_string(),
        num_recent_events: None,
        after: None,
    }).await?;
    
    println!("Events: {}", session.events().len());
    
    // Lister toutes les sessions
    let sessions = service.list(ListRequest {
        app_name: "demo".to_string(),
        user_id: "alice".to_string(),
    }).await?;
    
    println!("Total sessions: {}", sessions.len());
    
    // Supprimer la session
    service.delete(DeleteRequest {
        app_name: "demo".to_string(),
        user_id: "alice".to_string(),
        session_id: session.id().to_string(),
    }).await?;
    
    println!("Session deleted");
    
    Ok(())
}

Liens associés


Précédent : ← MCP Tools | Suivant : Gestion de l'état →