// src/agent/memory/age.rs // // 记忆时效性追踪 — 参考 Claude Code memdir/memoryAge.ts。 // // LLM 不擅长日期计算,"2026-01-15" 不会触发过时判断, // 但 "47 天前" 会。本模块提供人类可读的时效标签, // 注入到 system prompt 中以引导模型在使用记忆中之前核实。 use std::time::{SystemTime, UNIX_EPOCH}; /// 返回当前 Unix 时间戳(秒) pub fn now_secs() -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_secs() } /// 距离给定 Unix 时间戳的天数(向下取整)。 /// 0 = 今天, 1 = 昨天, 2+ = 更早。 /// 负数输入(未来时间/时钟偏差)截断为 0。 pub fn memory_age_days(mtime_secs: u64) -> u64 { let now = now_secs(); if mtime_secs >= now { return 0; } (now - mtime_secs) / 86_400 } /// 人类可读的时效标签。 pub fn memory_age_label(mtime_secs: u64) -> String { let days = memory_age_days(mtime_secs); match days { 0 => "今天".to_string(), 1 => "昨天".to_string(), n => format!("{} 天前", n), } } /// 返回时效警告文本,如果记忆超过 1 天则返回 Some。 /// 新鲜记忆(今天/昨天)返回 None — 此时警告只是噪音。 pub fn memory_freshness_text(mtime_secs: u64) -> Option { let days = memory_age_days(mtime_secs); if days <= 1 { return None; } Some(format!( "此记忆已有 {} 天。记忆是时间点快照,不是实时状态 — \ 关于代码行为或文件:行号的声明可能已过时。\ 请在断言为事实前与当前代码进行核对。", days )) } /// 包裹在 标签中的时效注释。 /// 对于 ≤ 1 天的记忆返回空字符串。 pub fn memory_freshness_note(mtime_secs: u64) -> String { match memory_freshness_text(mtime_secs) { Some(text) => format!("{}", text), None => String::new(), } } #[cfg(test)] mod tests { use super::*; #[test] fn test_age_days_today() { let now = now_secs(); assert_eq!(memory_age_days(now), 0); assert_eq!(memory_age_days(now - 100), 0); // 100 秒前仍是今天 } #[test] fn test_age_days_yesterday() { let now = now_secs(); assert_eq!(memory_age_days(now - 86_400), 1); assert_eq!(memory_age_days(now - 86_400 - 100), 1); } #[test] fn test_age_days_older() { let now = now_secs(); assert_eq!(memory_age_days(now - 86_400 * 3), 3); assert_eq!(memory_age_days(now - 86_400 * 47), 47); } #[test] fn test_age_days_clamps_future_to_zero() { let future = now_secs() + 86_400 * 10; assert_eq!(memory_age_days(future), 0); } #[test] fn test_age_label() { let now = now_secs(); assert_eq!(memory_age_label(now), "今天"); assert_eq!(memory_age_label(now - 86_400), "昨天"); assert_eq!(memory_age_label(now - 86_400 * 5), "5 天前"); } #[test] fn test_freshness_text_none_for_fresh() { let now = now_secs(); assert!(memory_freshness_text(now).is_none()); // 今天 assert!(memory_freshness_text(now - 86_400).is_none()); // 昨天 } #[test] fn test_freshness_text_some_for_old() { let now = now_secs(); let text = memory_freshness_text(now - 86_400 * 2); assert!(text.is_some()); assert!(text.unwrap().contains("2 天")); } #[test] fn test_freshness_note_contains_tags() { let now = now_secs(); let note = memory_freshness_note(now - 86_400 * 3); assert!(note.contains("")); assert!(note.contains("")); } #[test] fn test_freshness_note_empty_for_fresh() { let now = now_secs(); assert_eq!(memory_freshness_note(now), ""); assert_eq!(memory_freshness_note(now - 86_400), ""); } }