//! conversation_memory_demo —— 对话记忆滑动窗口与隔离。 //! Required features: cargo run --example conversation_memory_demo --features "memory" //! //! 演示: //! 1. `ConversationMemoryConfig` 构造(SlidingWindow / Full 策略) //! 2. `add_message` 写入多角色消息(Message IR) //! 3. 滑动窗口自动淘汰旧消息 //! 4. Full 策略保留全部 //! 5. `get_history` / `len` / `clear` //! 6. 跨 session 数据隔离(共用 MemoryStore) //! //! 运行:`cargo run --example conversation_memory_demo` use std::sync::Arc; use agcore::llm::types::message::Message; use agcore::memory::{ConversationMemory, ConversationMemoryConfig, InMemoryStore, MemoryStrategy}; fn message_text(msg: &Message) -> &str { match msg { Message::User { content } | Message::System { content } | Message::Assistant { content } | Message::ToolResult { content, .. } => content .iter() .filter_map(|b| match b { agcore::llm::types::message::ContentBlock::Text { text } => Some(text.as_str()), _ => None, }) .next() .unwrap_or(""), Message::UserImage { .. } => "[image]", _ => "", } } #[tokio::main] async fn main() { // 1. 滑动窗口策略:写入 5 条但只保留最近 3 条 println!("=== SlidingWindow 策略(max_turns=3)==="); let store = Arc::new(InMemoryStore::new()); let config = ConversationMemoryConfig { strategy: MemoryStrategy::SlidingWindow, max_turns: 3, compact_config: None, }; let mut memory = ConversationMemory::new(store, "session-1", config); for i in 0..5 { memory .add_message(Message::user_text(format!("消息 {i}"))) .await .expect("写入失败"); } println!("写入 5 条 → len = {} (期望 3)", memory.len()); let history = memory.get_history(); for (i, msg) in history.iter().enumerate() { println!(" [{}] {}", i, message_text(msg)); } assert_eq!(memory.len(), 3); assert_eq!(message_text(&history[0]), "消息 2", "最旧应是消息 2"); assert_eq!(message_text(&history[2]), "消息 4", "最新应是消息 4"); // 2. Full 策略:保留全部 println!("\n=== Full 策略(max_turns=3)==="); let store2 = Arc::new(InMemoryStore::new()); let config2 = ConversationMemoryConfig { strategy: MemoryStrategy::Full, max_turns: 3, compact_config: None, }; let mut memory2 = ConversationMemory::new(store2, "session-2", config2); for i in 0..5 { memory2 .add_message(Message::user_text(format!("Full {i}"))) .await .unwrap(); } println!("写入 5 条 → len = {} (期望 5)", memory2.len()); assert_eq!(memory2.len(), 5); // 3. 多角色混合 + clear println!("\n=== 多角色写入 + clear ==="); let store3 = Arc::new(InMemoryStore::new()); let mut memory3 = ConversationMemory::new(store3, "session-3", ConversationMemoryConfig::default()); memory3 .add_message(Message::user_text("你好")) .await .unwrap(); memory3 .add_message(Message::assistant("你好!有什么可以帮你的吗?")) .await .unwrap(); memory3 .add_message(Message::user_text("今天天气怎么样?")) .await .unwrap(); memory3 .add_message(Message::assistant( "我无法查询实时天气,但你可以查看天气应用。", )) .await .unwrap(); println!( "写入 4 条多角色消息 → len = {}, 最后一条: {:?}", memory3.len(), message_text(memory3.get_history().last().unwrap()) ); assert_eq!(memory3.len(), 4); memory3.clear().await.unwrap(); println!( "clear 后 → len = {}, is_empty = {}", memory3.len(), memory3.is_empty() ); assert!(memory3.is_empty()); // 4. Session 隔离 println!("\n=== Session 隔离(共用 InMemoryStore)==="); let store4 = Arc::new(InMemoryStore::new()); let mut a = ConversationMemory::new(store4.clone(), "s-a", ConversationMemoryConfig::default()); let mut b = ConversationMemory::new(store4.clone(), "s-b", ConversationMemoryConfig::default()); a.add_message(Message::user_text("A 的消息")).await.unwrap(); b.add_message(Message::user_text("B 的消息")).await.unwrap(); println!( "A.len = {}, B.len = {} (期望 1/1,互不污染)", a.len(), b.len() ); assert_eq!(a.len(), 1); assert_eq!(b.len(), 1); println!("\n✓ conversation_memory_demo 完成"); }