状态管理
adk-rust 中的 Session 状态允许 Agent 存储和检索在对话轮次之间持久存在的数据。状态通过键前缀进行组织,这些前缀决定了数据的范围和生命周期。
概述
状态以键值对的形式存储,其中:
- 键是带有可选前缀的字符串
- 值是 JSON 值 (
serde_json::Value)
前缀系统实现了不同的作用域级别:
Session作用域: 默认,与单个Session绑定- 用户作用域: 在用户的所有
Session之间共享 - 应用作用域: 在应用程序的所有用户之间共享
- 临时作用域: 在每次调用后清除
State Trait
State trait 定义了状态访问的接口:
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>;
}
还有一个用于只读访问的 ReadonlyState trait:
pub trait ReadonlyState: Send + Sync {
fn get(&self, key: &str) -> Option<Value>;
fn all(&self) -> HashMap<String, Value>;
}
状态键前缀
adk-rust 使用三个键前缀来控制状态作用域:
| 前缀 | 常量 | 作用域 |
|---|---|---|
app: | KEY_PREFIX_APP | 在所有用户和 Session 之间共享 |
user: | KEY_PREFIX_USER | 在特定用户的所有 Session 之间共享 |
temp: | KEY_PREFIX_TEMP | 在每次调用后清除 |
| (无) | - | Session 作用域 (默认) |
app: - 应用状态
在应用程序的所有用户和 Session 之间共享的状态。
use adk_session::KEY_PREFIX_APP;
// KEY_PREFIX_APP = "app:"
let key = format!("{}settings", KEY_PREFIX_APP); // "app:settings"
用例:
- 应用程序配置
- 共享资源
- 全局计数器或统计数据
user: - 用户状态
在特定用户的所有 Session 之间共享的状态。
use adk_session::KEY_PREFIX_USER;
// KEY_PREFIX_USER = "user:"
let key = format!("{}preferences", KEY_PREFIX_USER); // "user:preferences"
用例:
- 用户偏好设置
- 用户资料数据
- 跨
Session用户上下文
temp: - 临时状态
每次调用后清除且不持久化的状态。
use adk_session::KEY_PREFIX_TEMP;
// KEY_PREFIX_TEMP = "temp:"
let key = format!("{}current_step", KEY_PREFIX_TEMP); // "temp:current_step"
用例:
- 中间计算结果
- 当前操作上下文
- 不应持久化的数据
无前缀 - Session 状态
没有前缀的键是 Session 作用域的(默认行为)。
let key = "conversation_topic"; // Session-scoped
用例:
- 对话上下文
Session特定的数据- 逐轮状态
设置初始状态
可以在创建 Session 时初始化状态:
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();
// 应用作用域状态
initial_state.insert(
format!("{}version", KEY_PREFIX_APP),
json!("1.0.0")
);
// 用户作用域状态
initial_state.insert(
format!("{}name", KEY_PREFIX_USER),
json!("Alice")
);
// Session 作用域状态
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(())
}
读取 State
通过 session 的 state() 方法访问 state:
let state = session.state();
// 获取特定键
if let Some(value) = state.get("topic") {
println!("Topic: {}", value);
}
// 获取应用范围的状态
if let Some(version) = state.get("app:version") {
println!("App version: {}", version);
}
// 获取所有状态
let all_state = state.all();
for (key, value) in all_state {
println!("{}: {}", key, value);
}
通过 Events 更新 State
State 通常通过 event actions 进行更新。当一个 event 被附加到 session 时,其 state_delta 会被应用:
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()
};
// 当此事件被附加时,状态会更新
service.append_event(session.id(), event).await?;
State 作用域行为
session service 会自动处理 state 作用域:
在 Session 创建时
- 提取
app:前缀的键 → 存储到 app state - 提取
user:前缀的键 → 存储到 user state - 剩余的键(除了
temp:)→ 存储到 session state - 合并所有作用域以返回 session
在 Session 检索时
- 为应用程序加载 app state
- 为用户加载 user state
- 加载 session state
- 合并所有作用域 (app → user → session)
在 Event 附加时
- 从 event 中提取 state delta
- 过滤掉
temp:键(不持久化) - 将
app:delta 应用到 app state - 将
user:delta 应用到 user state - 将剩余的 delta 应用到 session state
完整示例
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();
// 创建第一个会话并带有初始状态
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?;
// 为同一用户创建第二个会话
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?;
// 会话 2 继承了应用和用户状态
let s2_state = session2.state();
// 应用状态是共享的
assert_eq!(s2_state.get("app:theme"), Some(json!("dark")));
// 用户状态是共享的
assert_eq!(s2_state.get("user:language"), Some(json!("en")));
// 会话状态是独立的
assert_eq!(s2_state.get("context"), Some(json!("session2")));
println!("状态作用域工作正常!");
Ok(())
}
使用 State 进行指令模板化
可以使用 {key} 语法将 state 值注入 agent instructions:
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()?;
当 agent 运行时,{user:name}、{topic} 和 {user:language} 会被替换为 session state 中的值。
最佳实践
1. 使用适当的作用域
// ✅ 良好:用户偏好在用户作用域
"user:theme"
"user:timezone"
// ✅ 良好:会话特定上下文无需前缀
"current_task"
"conversation_summary"
// ✅ 良好:应用范围设置在应用作用域
"app:model_version"
"app:feature_flags"
// ❌ 不佳:会话作用域中的用户数据(会在会话之间丢失)
"user_preferences" // 应该使用 "user:preferences"
2. 对中间数据使用临时状态
// ✅ 良好:中间结果在临时作用域
"temp:search_results"
"temp:current_step"
// ❌ 不佳:中间数据不必要地持久化
"search_results" // 将保存到数据库
3. 保持状态键一致
// ✅ 良好:一致的命名约定
"user:preferences.theme"
"user:preferences.language"
// ❌ 不佳:不一致的命名
"user:theme"
"userLanguage"
"user-timezone"
相关
上一页: ← Sessions | 下一页: Callbacks →