Zustandsverwaltung
Der Sitzungszustand in ADK-Rust ermöglicht es Agents, Daten zu speichern und abzurufen, die über Konversationsrunden hinweg bestehen bleiben. Der Zustand wird mithilfe von Schlüsselpräfixen organisiert, die den Geltungsbereich und die Lebensdauer der Daten bestimmen.
Übersicht
Der Zustand wird als Schlüssel-Wert-Paare gespeichert, wobei gilt:
- Schlüssel sind Zeichenketten mit optionalen Präfixen
- Werte sind JSON-Werte (
serde_json::Value)
Das Präfixsystem ermöglicht verschiedene Geltungsbereiche:
- Sitzungsbezogen: Standard, an eine einzelne Sitzung gebunden
- Benutzerbezogen: Über alle Sitzungen eines Benutzers hinweg geteilt
- Anwendungsbezogen: Über alle Benutzer einer Anwendung hinweg geteilt
- Temporär: Nach jeder Aufrufung gelöscht
State Trait
Der State trait definiert die Schnittstelle für den Zustandszugriff:
use serde_json::Value;
use std::collections::HashMap;
pub trait State: Send + Sync {
/// Get a value by key
fn get(&self, key: &str) -> Option<Value>;
/// Set a value
fn set(&mut self, key: String, value: Value);
/// Get all state as a map
fn all(&self) -> HashMap<String, Value>;
}
Es gibt auch einen ReadonlyState trait für den schreibgeschützten Zugriff:
pub trait ReadonlyState: Send + Sync {
fn get(&self, key: &str) -> Option<Value>;
fn all(&self) -> HashMap<String, Value>;
}
Schlüsselpräfixe für den Zustand
ADK-Rust verwendet drei Schlüsselpräfixe zur Steuerung des Zustands-Scoping:
| Präfix | Konstante | Geltungsbereich |
|---|---|---|
app: | KEY_PREFIX_APP | Für alle Benutzer und Sitzungen freigegeben |
user: | KEY_PREFIX_USER | Für alle Sitzungen eines Benutzers freigegeben |
temp: | KEY_PREFIX_TEMP | Nach jedem Aufruf gelöscht |
| (none) | - | Sitzungsbezogen (Standard) |
app: - Anwendungszustand
Zustand, der über alle Benutzer und Sitzungen einer Anwendung hinweg geteilt wird.
use adk_session::KEY_PREFIX_APP;
// KEY_PREFIX_APP = "app:"
let key = format!("{}settings", KEY_PREFIX_APP); // "app:settings"
Anwendungsfälle:
- Anwendungskonfiguration
- Geteilte Ressourcen
- Globale Zähler oder Statistiken
user: - Benutzerzustand
Zustand, der über alle Sitzungen für einen bestimmten Benutzer hinweg geteilt wird.
use adk_session::KEY_PREFIX_USER;
// KEY_PREFIX_USER = "user:"
let key = format!("{}preferences", KEY_PREFIX_USER); // "user:preferences"
Anwendungsfälle:
- Benutzereinstellungen
- Benutzerprofildaten
- Benutzerkontext über Sitzungen hinweg
temp: - Temporärer Zustand
Zustand, der nach jedem Aufruf gelöscht wird. Nicht persistent.
use adk_session::KEY_PREFIX_TEMP;
// KEY_PREFIX_TEMP = "temp:"
let key = format!("{}current_step", KEY_PREFIX_TEMP); // "temp:current_step"
Anwendungsfälle:
- Zwischenergebnisse von Berechnungen
- Aktueller Operationskontext
- Daten, die nicht persistent sein sollen
Kein Präfix - Sitzungszustand
Schlüssel ohne Präfix sind sitzungsbezogen (Standardverhalten).
let key = "conversation_topic"; // Session-scoped
Anwendungsfälle:
- Konversationskontext
- Sitzungsspezifische Daten
- Zug-für-Zug-Zustand
Initialen Zustand festlegen
Der Zustand kann beim Erstellen einer Sitzung initialisiert werden:
use adk_session::{InMemorySessionService, SessionService, CreateRequest, KEY_PREFIX_APP, KEY_PREFIX_USER};
use serde_json::json;
use std::collections::HashMap;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut initial_state = HashMap::new();
// App-scoped state
initial_state.insert(
format!("{}version", KEY_PREFIX_APP),
json!("1.0.0")
);
// User-scoped state
initial_state.insert(
format!("{}name", KEY_PREFIX_USER),
json!("Alice")
);
// Session-scoped state
initial_state.insert(
"topic".to_string(),
json!("Getting started")
);
let service = InMemorySessionService::new();
let session = service.create(CreateRequest {
app_name: "my_app".to_string(),
user_id: "user_123".to_string(),
session_id: None,
state: initial_state,
}).await?;
Ok(())
}
Zustand lesen
Greifen Sie auf den Zustand über die Session-Methode state() zu:
let state = session.state();
// Get a specific key
if let Some(value) = state.get("topic") {
println!("Topic: {}", value);
}
// Get app-scoped state
if let Some(version) = state.get("app:version") {
println!("App version: {}", version);
}
// Get all state
let all_state = state.all();
for (key, value) in all_state {
println!("{}: {}", key, value);
}
Zustandsaktualisierungen über Ereignisse
Der Zustand wird typischerweise durch Ereignisaktionen aktualisiert. Wenn ein Ereignis an eine Session angehängt wird, wird dessen state_delta angewendet:
use adk_session::{Event, EventActions};
use serde_json::json;
use std::collections::HashMap;
let mut state_delta = HashMap::new();
state_delta.insert("counter".to_string(), json!(42));
state_delta.insert("user:last_seen".to_string(), json!("2024-01-15"));
let mut event = Event::new("invocation_123");
event.actions = EventActions {
state_delta,
..Default::default()
};
// When this event is appended, state is updated
service.append_event(session.id(), event).await?;
Zustands-Scoping-Verhalten
Der Session-Dienst handhabt das Zustands-Scoping automatisch:
Bei der Session-Erstellung
- Extrahieren Sie
app:präfixierte Schlüssel → Speichern Sie im App-Zustand - Extrahieren Sie
user:präfixierte Schlüssel → Speichern Sie im Benutzerzustand - Verbleibende Schlüssel (außer
temp:) → Speichern Sie im Session-Zustand - Führen Sie alle Scopes für die zurückgegebene Session zusammen
Beim Abrufen der Session
- Laden Sie den App-Zustand für die Anwendung
- Laden Sie den Benutzerzustand für den Benutzer
- Laden Sie den Session-Zustand
- Führen Sie alle Scopes zusammen (app → user → session)
Beim Anhängen eines Ereignisses
- Zustandsdelta aus Ereignis extrahieren
temp:Schlüssel herausfiltern (nicht persistent)app:Deltas auf den App-Zustand anwendenuser:Deltas auf den Benutzerzustand anwenden- Verbleibende Deltas auf den Sitzungszustand anwenden
Vollständiges Beispiel
use adk_session::{
InMemorySessionService, SessionService, CreateRequest, GetRequest,
KEY_PREFIX_APP, KEY_PREFIX_USER,
};
use serde_json::json;
use std::collections::HashMap;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let service = InMemorySessionService::new();
// Create first session with initial state
let mut state1 = HashMap::new();
state1.insert(format!("{}theme", KEY_PREFIX_APP), json!("dark"));
state1.insert(format!("{}language", KEY_PREFIX_USER), json!("en"));
state1.insert("context".to_string(), json!("session1"));
let session1 = service.create(CreateRequest {
app_name: "my_app".to_string(),
user_id: "alice".to_string(),
session_id: Some("s1".to_string()),
state: state1,
}).await?;
// Create second session for same user
let mut state2 = HashMap::new();
state2.insert("context".to_string(), json!("session2"));
let session2 = service.create(CreateRequest {
app_name: "my_app".to_string(),
user_id: "alice".to_string(),
session_id: Some("s2".to_string()),
state: state2,
}).await?;
// Session 2 inherits app and user state
let s2_state = session2.state();
// App state is shared
assert_eq!(s2_state.get("app:theme"), Some(json!("dark")));
// User state is shared
assert_eq!(s2_state.get("user:language"), Some(json!("en")));
// Session state is separate
assert_eq!(s2_state.get("context"), Some(json!("session2")));
println!("State scoping works correctly!");
Ok(())
}
Anweisungs-Templating mit Zustand
Zustandswerte können unter Verwendung der {key} Syntax in Agent-Anweisungen injiziert werden:
use adk_rust::prelude::*;
use std::sync::Arc;
let agent = LlmAgentBuilder::new("personalized_assistant")
.instruction("You are helping {user:name} with {topic}. Their preferred language is {user:language}.")
.model(Arc::new(model))
.build()?;
Wenn der Agent läuft, werden {user:name}, {topic} und {user:language} durch Werte aus dem Sitzungszustand ersetzt.
Bewährte Methoden
1. Geeignete Geltungsbereiche verwenden
// ✅ Good: User preferences in user scope
"user:theme"
"user:timezone"
// ✅ Good: Session-specific context without prefix
"current_task"
"conversation_summary"
// ✅ Good: App-wide settings in app scope
"app:model_version"
"app:feature_flags"
// ❌ Bad: User data in session scope (lost between sessions)
"user_preferences" // Should be "user:preferences"
2. Temporären Zustand für Zwischenergebnisse verwenden
// ✅ Good: Intermediate results in temp scope
"temp:search_results"
"temp:current_step"
// ❌ Bad: Intermediate data persisted unnecessarily
"search_results" // Will be saved to database
3. Zustandsschlüssel konsistent halten
// ✅ Good: Consistent naming convention
"user:preferences.theme"
"user:preferences.language"
// ❌ Bad: Inconsistent naming
"user:theme"
"userLanguage"
"user-timezone"
Verwandt
- Sessions - Übersicht über die Session-Verwaltung
- Events - Ereignisstruktur und state_delta
- LlmAgent - Anweisungs-Templating
Zurück: ← Sessions | Weiter: Callbacks →