6.4 ProviderState 状态持久化
`ProviderState<T>` 是 RAF 为上下文提供器设计的类型安全状态访问器。它封装了 `ISession::get_provider_state()` 和 `set_provider_state()` 的原始 JSON 操作,提供编译期类型检查、自动序列化/反序列化和便捷的 `get_or_init()` 模式。
ProviderState 结构
/// 类型安全的 Provider 状态访问器
///
/// 为 Provider 提供类型安全的 Session 状态读写能力,
/// 避免手动序列化/反序列化和 key 拼写错误。
pub struct ProviderState<T: Serialize + DeserializeOwned + Default> {
key: String,
_marker: PhantomData<T>,
}
impl<T: Serialize + DeserializeOwned + Default> ProviderState<T> {
/// 创建指定 Provider 的类型安全状态访问器
pub fn new(provider_name: &str) -> Self {
Self {
key: format!("provider_state::{}", provider_name),
_marker: PhantomData,
}
}
/// 获取或初始化 Provider 状态
///
/// 如果 Session 中存在该 Provider 的状态,反序列化返回;
/// 否则返回 T 的默认值。
pub fn get_or_init(&self, session: &dyn ISession) -> T {
session
.get_provider_state(&self.key)
.ok()
.and_then(|v| serde_json::from_value::<T>(v).ok())
.unwrap_or_default()
}
/// 保存 Provider 状态到 Session
pub fn save(&self, session: &dyn ISession, state: &T) -> Result<()> {
let value = serde_json::to_value(state)
.map_err(|e| AgentError::Serialize(e.to_string()))?;
session.set_provider_state(&self.key, value)
}
}
**类型约束**:
- `Serialize + DeserializeOwned`:状态必须可以被 JSON 序列化/反序列化 - `Default`:`get_or_init()` 在状态不存在时返回默认值
**key 格式**:`provider_state::<ProviderName>`,确保不同 Provider 的状态不会互相覆盖。
使用模式
基本模式
use rust_agent_core::ProviderState;
use serde::{Serialize, Deserialize};
// 1. 定义状态类型
#[derive(Default, Serialize, Deserialize)]
struct MyState {
call_count: u64,
last_result_summary: String,
}
// 2. 创建访问器
let state_key = ProviderState::<MyState>::new("MyProvider");
// 3. 在 on_invoking 中读取
let mut state = state_key.get_or_init(session);
state.call_count += 1;
// 4. 修改后保存
state_key.save(session, &state)?;
完整示例:计数器 Provider
use async_trait::async_trait;
use rust_agent_core::{
AgentResponse, AgentRunOptions, ChatMessage, ContextResult,
IAgent, IContextProvider, ISession, ProviderState, Result,
};
use serde::{Deserialize, Serialize};
/// 计数器状态
#[derive(Debug, Default, Serialize, Deserialize)]
struct CounterState {
invocation_count: u64,
total_tokens_since_start: u64,
}
/// 记录调用次数的提供器
pub struct CounterProvider;
#[async_trait]
impl IContextProvider for CounterProvider {
fn name(&self) -> &str { "CounterProvider" }
fn kind(&self) -> &str { "custom" }
async fn on_invoking(
&self,
_agent: &dyn IAgent,
session: &dyn ISession,
_messages: &[ChatMessage],
_options: &AgentRunOptions,
) -> Result<ContextResult> {
let state_key = ProviderState::<CounterState>::new("CounterProvider");
// 加载或初始化状态
let mut state = state_key.get_or_init(session);
state.invocation_count += 1;
// 保存更新后的状态
state_key.save(session, &state)?;
Ok(ContextResult {
instructions: Some(format!(
"[系统] 这是本次会话的第 {} 次调用。",
state.invocation_count
)),
..Default::default()
})
}
async fn on_invoked(
&self,
_agent: &dyn IAgent,
session: &dyn ISession,
_request_messages: &[ChatMessage],
response: Option<&AgentResponse>,
_error: Option<&AgentError>,
) -> Result<()> {
// 累计 token 用量
if let Some(resp) = response {
if let Some(ref result) = resp.result {
if let Some(ref usage) = result.usage {
let state_key = ProviderState::<CounterState>::new("CounterProvider");
let mut state = state_key.get_or_init(session);
state.total_tokens_since_start += usage.total_tokens;
state_key.save(session, &state)?;
}
}
}
Ok(())
}
}
实际案例:WorkspaceState
RAF 的 `WorkspaceContextProvider` 使用 `ProviderState` 持久化工作区配置:
#[derive(Default, Serialize, Deserialize)]
struct WorkspaceState {
scope_name: String,
scope_root: String,
policy: String,
}
// 在 on_invoking 中
let state = ProviderState::<WorkspaceState>::new("WorkspaceContextProvider");
let ws = state.get_or_init(session);
if ws.scope_name.is_empty() {
// 首次调用:保存工作区配置
let _ = state.save(
session,
&WorkspaceState {
scope_name: self.scope.name.clone(),
scope_root: self.scope.root.to_string_lossy().to_string(),
policy: format!("{:?}", self.scope.policy),
},
);
}
这样,即使 Agent 在多次 `run()` 调用之间重启(结合 `FileSystemSessionStore`),工作区配置也能从 Session 中恢复。
与直接调用 ISession 的对比
**不使用 ProviderState(原始方式):**
// 读取 — 容易出错
let raw = session.get_provider_state("provider_state::MyProvider")?;
let state: MyState = if let Some(v) = raw.as_object() {
serde_json::from_value(raw)?
} else {
MyState::default()
};
// 写入 — key 容易拼错
session.set_provider_state(
"provider_state::MyProvider",
serde_json::to_value(&state)?
)?;
**使用 ProviderState(推荐方式):**
// 一行读取
let state = ProviderState::<MyState>::new("MyProvider")
.get_or_init(session);
// 一行写入
ProviderState::<MyState>::new("MyProvider")
.save(session, &state)?;
**优势**:
| 特性 | 原始方式 | ProviderState | |------|----------|---------------| | 类型安全 | ❌ 运行时错误 | ✅ 编译期检查 | | Key 一致性 | ❌ 易拼错 | ✅ 自动生成 | | 默认值 | ❌ 手动处理 | ✅ `get_or_init()` | | 代码量 | 5–8 行 | 1–2 行 |
状态生命周期
sequenceDiagram
participant P as Provider
participant PS as ProviderState<T>
participant S as ISession
participant Store as ProviderStateStore
Note over P: 首次调用
P->>PS: get_or_init(session)
PS->>S: get_provider_state("provider_state::MyProv")
S->>Store: get("provider_state::MyProv")
Store-->>S: None
S-->>PS: null
PS-->>P: T::default()
P->>P: 修改状态
P->>PS: save(session, &state)
PS->>PS: serde_json::to_value(state)
PS->>S: set_provider_state("provider_state::MyProv", value)
S->>Store: set("provider_state::MyProv", value)
Note over P: 后续调用
P->>PS: get_or_init(session)
PS->>S: get_provider_state("provider_state::MyProv")
S->>Store: get("provider_state::MyProv")
Store-->>S: Some(value)
S-->>PS: value
PS->>PS: serde_json::from_value::<T>(value)
PS-->>P: T (已保存的状态)
注意事项
1. **`get_or_init()` 不自动保存**——如果状态是 `T::default()` 且你希望它持久化,需要显式调用 `save()` 2. **反序列化失败降级为默认值**——如果会话中存储的状态 JSON 与 `T` 不匹配,`get_or_init()` 返回 `T::default()` 而不报错 3. **key 是全局唯一的**——`"provider_state::"` 前缀确保不会与框架其他内部 key 冲突 4. **状态随会话存储**——使用 `InMemorySessionStore` 时状态不持久化;使用 `FileSystemSessionStore` 时状态随会话 JSON 文件持久化
关键要点
1. **`ProviderState<T>` 消除样板代码**——类型安全的单行存取器替代手写 JSON 操作 2. **`get_or_init()` 是最常用模式**——首次返回默认值,后续返回已保存状态 3. **key 自动生成**——`format!("provider_state::{}", provider_name)` 确保唯一性 4. **反序列化失败降级**——状态格式变更时不会导致 Agent 调用失败 5. **WorkspaceContextProvider 是最佳实践案例**——首次调用保存配置,后续调用跳过