6.2 AgentSession 默认实现
`AgentSession` 是 `ISession` 的默认内存实现。它使用 `RwLock` 保护并发访问,自动生成 UUID 作为会话 ID,并提供完整的消息管理、元数据追踪和序列化支持。
结构定义
/// 默认的内存会话实现
pub struct AgentSession {
session_id: String, // UUID v4
history: RwLock<Vec<ChatMessage>>, // 消息历史
metadata: RwLock<SessionMetadata>, // 会话元数据
provider_states: RwLock<ProviderStateStore>, // Provider 状态存储
created_at: DateTime<Utc>, // 创建时间(不可变)
last_active_at: RwLock<DateTime<Utc>>, // 最后活动时间
}
**并发策略**:
- 每个字段独立使用 `RwLock` 保护,降低锁竞争 - `RwLock` 而非 `Mutex`:消息读取远多于写入,读写锁更高效 - `created_at` 不可变,无需锁
构造函数
impl AgentSession {
/// 创建新的内存会话,自动生成随机 UUID 作为会话 ID
pub fn new() -> Self {
let now = Utc::now();
Self {
session_id: Uuid::new_v4().to_string(),
history: RwLock::new(Vec::new()),
metadata: RwLock::new(SessionMetadata {
created_at: now,
updated_at: now,
message_count: 0,
last_request_hash: None,
}),
provider_states: RwLock::new(ProviderStateStore::new()),
created_at: now,
last_active_at: RwLock::new(now),
}
}
/// 使用指定 ID 创建新的内存会话
pub fn with_id(session_id: impl Into<String>) -> Self {
let now = Utc::now();
Self {
session_id: session_id.into(),
// ... 同 new()
}
}
}
impl Default for AgentSession {
fn default() -> Self { Self::new() }
}
**`new()` vs `with_id()`**:`new()` 适用于全新会话(自动生成 UUID);`with_id()` 适用于恢复已有会话(如从文件系统反序列化后)。
ISession 实现详解
消息操作
#[async_trait]
impl ISession for AgentSession {
fn session_id(&self) -> &str { &self.session_id }
async fn add_message(&self, message: ChatMessage) -> Result<()> {
self.history.write().await.push(message);
let mut meta = self.metadata.write().await;
meta.message_count += 1;
meta.updated_at = Utc::now();
Ok(())
}
async fn get_messages(&self) -> Result<Vec<ChatMessage>> {
Ok(self.history.read().await.clone()) // 返回完整副本
}
async fn clear(&self) -> Result<()> {
self.history.write().await.clear();
let mut meta = self.metadata.write().await;
meta.message_count = 0;
meta.updated_at = Utc::now();
Ok(())
}
}
**设计考量**:
- `get_messages()` 返回完整副本——牺牲内存效率换取接口简洁性,避免引用生命周期复杂化 - `add_message()` 和 `clear()` 同时更新元数据——保证 `message_count` 和 `updated_at` 的一致性
批量操作
async fn add_messages_batch(&self, messages: &[ChatMessage]) -> Result<()> {
let count = messages.len() as u64;
{
let mut history = self.history.write().await;
history.extend(messages.iter().cloned());
}
let mut meta = self.metadata.write().await;
meta.message_count += count;
meta.updated_at = Utc::now();
Ok(())
}
fn get_message_count(&self) -> usize {
self.metadata.try_read()
.map(|m| m.message_count as usize)
.unwrap_or(0)
}
**优化点**:
- `add_messages_batch` 覆盖默认实现,一次性持有写锁完成所有追加(而非逐条加锁) - `get_message_count` 实现为 O(1) 零克隆——读 `metadata` 中的 `message_count` 而非遍历 `history`
元数据与快照
fn metadata(&self) -> SessionMetadata {
self.metadata.try_read()
.map(|m| m.clone())
.unwrap_or_default()
}
fn snapshot(&self) -> SessionSnapshot {
// 注意:非原子一致性——各字段通过独立 try_read() 获取
let history = self.history.try_read()
.map(|h| h.clone()).unwrap_or_default();
let meta = self.metadata.try_read()
.map(|m| m.clone()).unwrap_or_default();
let ps = self.provider_states.try_read()
.map(|p| p.clone()).unwrap_or_default();
SessionSnapshot {
session_id: self.session_id.clone(),
metadata: SessionMetadata {
created_at: meta.created_at,
updated_at: meta.updated_at,
message_count: meta.message_count,
last_request_hash: meta.last_request_hash,
},
messages: history,
provider_states: ps,
last_active_at: self.last_active_at.try_read().ok().map(|t| *t),
}
}
**快照的非原子性**:由于每个字段通过独立的 `try_read()` 读取,快照不是原子一致的。消息、元数据和 Provider 状态可能反映略有不同的时间点。框架在设计上接受这种最终一致性。
序列化
fn serialize(&self) -> Result<String> {
serde_json::to_string(&self.snapshot())
.map_err(|e| AgentError::Serialize(e.to_string()))
}
fn deserialize(data: &str) -> Result<Self> {
let snap: SessionSnapshot = serde_json::from_str(data)
.map_err(|e| AgentError::Serialize(e.to_string()))?;
let created_at = snap.metadata.created_at;
let last_active = snap.last_active_at.unwrap_or(created_at);
Ok(Self {
session_id: snap.session_id,
history: RwLock::new(snap.messages),
metadata: RwLock::new(snap.metadata),
provider_states: RwLock::new(snap.provider_states),
created_at,
last_active_at: RwLock::new(last_active),
})
}
**序列化路径**:通过 `snapshot()` → `SessionSnapshot` → `serde_json`,所有字段都是 JSON 可序列化的。
请求哈希追踪(KV 缓存优化)
fn touch_request_hash(&self, messages: &[ChatMessage]) {
let hash = hash_messages(messages);
if let Ok(mut meta) = self.metadata.try_write() {
meta.last_request_hash = Some(hash);
}
}
fn get_last_request_hash(&self) -> Option<u64> {
self.metadata.try_read().ok().and_then(|m| m.last_request_hash)
}
**哈希算法**:
fn hash_messages(messages: &[ChatMessage]) -> u64 {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
for msg in messages {
std::mem::discriminant(&msg.role).hash(&mut hasher);
msg.content.hash(&mut hasher);
if let Some(ref tc) = msg.tool_calls {
for call in tc {
call.id.hash(&mut hasher);
call.name.hash(&mut hasher);
call.arguments.as_str().unwrap_or("").hash(&mut hasher);
}
}
if let Some(ref tcid) = msg.tool_call_id {
tcid.hash(&mut hasher);
}
}
hasher.finish()
}
哈希基于消息的关键字段:role、content、tool_calls(id + name + arguments)、tool_call_id。新请求可以比较消息前缀与前次请求的哈希,如果前缀匹配则可能复用 KV 缓存。
TTL 支持
fn created_at(&self) -> DateTime<Utc> {
self.created_at
}
fn last_active_at(&self) -> DateTime<Utc> {
self.last_active_at.try_read().map(|t| *t).unwrap_or(self.created_at)
}
async fn touch_last_active(&self) {
let mut t = self.last_active_at.write().await;
*t = Utc::now();
}
Provider 状态
fn get_provider_state(&self, provider_name: &str) -> Result<serde_json::Value> {
if let Ok(ps) = self.provider_states.try_read() {
Ok(ps.get(provider_name).cloned().unwrap_or(serde_json::Value::Null))
} else {
Ok(serde_json::Value::Null)
}
}
fn set_provider_state(&self, provider_name: &str, state: serde_json::Value) -> Result<()> {
if let Ok(mut ps) = self.provider_states.try_write() {
ps.set(provider_name, state);
}
Ok(())
}
ProviderStateStore
/// Provider 级 Session 状态存储
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ProviderStateStore {
states: HashMap<String, serde_json::Value>,
}
impl ProviderStateStore {
pub fn new() -> Self { Self { states: HashMap::new() } }
pub fn get(&self, provider_name: &str) -> Option<&serde_json::Value> {
self.states.get(provider_name)
}
pub fn set(&mut self, provider_name: &str, state: serde_json::Value) {
self.states.insert(provider_name.to_string(), state);
}
pub fn remove(&mut self, provider_name: &str) {
self.states.remove(provider_name);
}
}
这是一个按 Provider 名称索引的通用 JSON 状态存储。每个 Provider 可以在此存储任意 JSON 值,框架不关心内容的具体结构。
关键要点
1. **每个字段独立 RwLock**——降低锁竞争,提升并发性能 2. **`get_messages()` 返回完整副本**——简单可靠,适合典型对话规模(数百条消息) 3. **快照非原子一致**——接受最终一致性,避免全局锁 4. **批量操作一次持锁**——`add_messages_batch` 一次性完成所有追加 5. **request_hash 支持 KV 缓存优化**——对现代 LLM 提供商的性能至关重要