Agent也会“记错“:智能体记忆的遗忘机制设计(重要性评分+时间衰减+敏感过滤,Python 3.10+实现)

Agent也会"记错":智能体记忆的遗忘机制设计(重要性评分+时间衰减+敏感过滤,Python 3.10+实现)

导读 :你的Agent记住了很多东西,但有没有想过------它记住的东西可能是错的、旧的、或者不该记的 ?本文从Loop Engineering 的"状态管理"视角出发,用可运行的Python代码 实现三层遗忘机制:重要性评分淘汰 + 时间衰减降权 + 敏感信息自动过滤

📅 本文版本:2026年7月 · Python 3.10+ · 仅依赖标准库(无需外部包)

文章目录

  • [Agent也会"记错":智能体记忆的遗忘机制设计(重要性评分+时间衰减+敏感过滤,Python 3.10+实现)](#Agent也会"记错":智能体记忆的遗忘机制设计(重要性评分+时间衰减+敏感过滤,Python 3.10+实现))
    • 一、前置条件与版本说明
    • 二、为什么Agent需要"遗忘"?三个被忽视的危险信号
      • [2.1 记忆过多 = 检索噪音](#2.1 记忆过多 = 检索噪音)
      • [2.2 敏感信息泄露风险](#2.2 敏感信息泄露风险)
      • [2.3 过时信息误导决策](#2.3 过时信息误导决策)
    • 三、三层遗忘机制:从"记住一切"到"智能筛选"
    • [四、Layer 1:敏感过滤------危险信息不进记忆库](#四、Layer 1:敏感过滤——危险信息不进记忆库)
    • [五、Layer 2:重要性评分------常用记忆保留,冷门记忆淘汰](#五、Layer 2:重要性评分——常用记忆保留,冷门记忆淘汰)
    • [六、Layer 3:时间衰减------旧记忆自动"褪色"](#六、Layer 3:时间衰减——旧记忆自动"褪色")
    • 七、整合三层:智能遗忘管理器
    • [八、与Loop Engineering和内容矩阵的关联](#八、与Loop Engineering和内容矩阵的关联)
      • [8.1 与Loop Engineering的关联](#8.1 与Loop Engineering的关联)
      • [8.2 与已有内容矩阵的闭环](#8.2 与已有内容矩阵的闭环)
    • 九、总结
    • 十、方案对比与选型建议
      • [10.1 三种衰减模型对比](#10.1 三种衰减模型对比)
      • [10.2 三层遗忘机制选型指南](#10.2 三层遗忘机制选型指南)
      • [10.3 适用边界说明](#10.3 适用边界说明)

一、前置条件与版本说明

⚠️ 本文环境 :Python 3.10+(推荐3.11+),re / math / datetime 均为标准库,无需 pip install

⚠️ 适用范围 :本文遗忘机制代码适用于所有基于向量或关键词检索的Agent Memory系统

⚠️ 生产环境提示:Demo代码为教学目的设计,生产环境需补充:持久化存储(SQLite/Redis)、分布式锁、日志审计

bash 复制代码
# 验证Python版本(需要3.7+,推荐3.11+)
python --version
# 预期输出:Python 3.11.x 或 3.10.x
python 复制代码
# 验证标准库可用性
import re, math, datetime
print(f"re: {re.__name__}, math: {math.__name__}, datetime: {datetime.__name__}")
# 预期输出:re: re, math: math, datetime: datetime

二、为什么Agent需要"遗忘"?三个被忽视的危险信号

2.1 记忆过多 = 检索噪音

Agent Memory的理想假设是"记住越多,回答越好"。但现实中:

当Agent的记忆库里塞了1000条记录,用户问"我的偏好是什么",向量检索返回的前5条可能包含3条无关的过期信息------记忆越多,检索精度反而越低

这被称为**"记忆诅咒"**(Curse of Memory):与"维度诅咒"类似,记忆空间膨胀后,语义检索的区分度下降。

2.2 敏感信息泄露风险

Agent记住了一条记忆:

"用户张三的密码是MyP@ssw0rd123,保存在文件/home/zhangsan/.env里"

几个月后,另一个用户(或攻击者通过Prompt注入)问Agent:"之前用户存过什么重要信息?"------Agent可能通过语义检索把这条密码返回到LLM上下文中。

问题不是"记住",而是"没有忘记"

2.3 过时信息误导决策

Agent记住了:

"公司Q2销售目标是1000万"

但到了Q3,目标已经调整为1200万。Agent如果没有"忘记"旧目标,仍然基于1000万给出建议,导致决策偏差。

危险信号 表现 后果
记忆诅咒 检索返回大量无关记忆 回答质量下降、token浪费
敏感泄露 密码/密钥被语义检索命中 安全事件、合规风险
过时误导 旧规则/旧数据影响当前决策 业务损失、用户不满
存储膨胀 记忆库无限增长 成本上升、检索速度下降

三、三层遗忘机制:从"记住一切"到"智能筛选"

我设计的三层遗忘机制,对应人脑记忆的不同类型:
#mermaid-svg-NPk1NVl86afrY83c{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NPk1NVl86afrY83c .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NPk1NVl86afrY83c .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NPk1NVl86afrY83c .error-icon{fill:#552222;}#mermaid-svg-NPk1NVl86afrY83c .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NPk1NVl86afrY83c .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NPk1NVl86afrY83c .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NPk1NVl86afrY83c .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NPk1NVl86afrY83c .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NPk1NVl86afrY83c .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NPk1NVl86afrY83c .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NPk1NVl86afrY83c .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NPk1NVl86afrY83c .marker.cross{stroke:#333333;}#mermaid-svg-NPk1NVl86afrY83c svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NPk1NVl86afrY83c p{margin:0;}#mermaid-svg-NPk1NVl86afrY83c .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NPk1NVl86afrY83c .cluster-label text{fill:#333;}#mermaid-svg-NPk1NVl86afrY83c .cluster-label span{color:#333;}#mermaid-svg-NPk1NVl86afrY83c .cluster-label span p{background-color:transparent;}#mermaid-svg-NPk1NVl86afrY83c .label text,#mermaid-svg-NPk1NVl86afrY83c span{fill:#333;color:#333;}#mermaid-svg-NPk1NVl86afrY83c .node rect,#mermaid-svg-NPk1NVl86afrY83c .node circle,#mermaid-svg-NPk1NVl86afrY83c .node ellipse,#mermaid-svg-NPk1NVl86afrY83c .node polygon,#mermaid-svg-NPk1NVl86afrY83c .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NPk1NVl86afrY83c .rough-node .label text,#mermaid-svg-NPk1NVl86afrY83c .node .label text,#mermaid-svg-NPk1NVl86afrY83c .image-shape .label,#mermaid-svg-NPk1NVl86afrY83c .icon-shape .label{text-anchor:middle;}#mermaid-svg-NPk1NVl86afrY83c .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NPk1NVl86afrY83c .rough-node .label,#mermaid-svg-NPk1NVl86afrY83c .node .label,#mermaid-svg-NPk1NVl86afrY83c .image-shape .label,#mermaid-svg-NPk1NVl86afrY83c .icon-shape .label{text-align:center;}#mermaid-svg-NPk1NVl86afrY83c .node.clickable{cursor:pointer;}#mermaid-svg-NPk1NVl86afrY83c .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NPk1NVl86afrY83c .arrowheadPath{fill:#333333;}#mermaid-svg-NPk1NVl86afrY83c .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NPk1NVl86afrY83c .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NPk1NVl86afrY83c .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NPk1NVl86afrY83c .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NPk1NVl86afrY83c .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NPk1NVl86afrY83c .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NPk1NVl86afrY83c .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NPk1NVl86afrY83c .cluster text{fill:#333;}#mermaid-svg-NPk1NVl86afrY83c .cluster span{color:#333;}#mermaid-svg-NPk1NVl86afrY83c div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NPk1NVl86afrY83c .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NPk1NVl86afrY83c rect.text{fill:none;stroke-width:0;}#mermaid-svg-NPk1NVl86afrY83c .icon-shape,#mermaid-svg-NPk1NVl86afrY83c .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NPk1NVl86afrY83c .icon-shape p,#mermaid-svg-NPk1NVl86afrY83c .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NPk1NVl86afrY83c .icon-shape .label rect,#mermaid-svg-NPk1NVl86afrY83c .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NPk1NVl86afrY83c .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NPk1NVl86afrY83c .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NPk1NVl86afrY83c :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

新记忆写入
Layer 1: 敏感过滤

立即检查敏感信息
是否敏感?
拒绝存储/加密存储

或设置短TTL
Layer 2: 重要性评分

记录访问频率和显式标记
Layer 3: 时间衰减

旧记忆自动降权
定期清理

低分+过期记忆淘汰

层级 机制 类比人脑 作用时机 解决什么问题
Layer 1 敏感过滤 选择性注意 写入时 密码/隐私不进入长期记忆
Layer 2 重要性评分 重复强化 每次访问时 常用记忆保留,冷门记忆淘汰
Layer 3 时间衰减 记忆淡化 定期批量 旧记忆自动降权,新记忆优先

四、Layer 1:敏感过滤------危险信息不进记忆库

目标:在记忆写入的第一时间,检测并拦截敏感信息。

python 复制代码
from typing import Dict, List, Tuple
import re
from enum import Enum

class SensitivityLevel(Enum):
    """数据敏感度分级(GB/Z 185 6.2)。"""
    PUBLIC = "public"           # 公开
    INTERNAL = "internal"      # 内部
    CONFIDENTIAL = "confidential"  # 机密
    RESTRICTED = "restricted"    # 受限(应立即拦截)

class SensitiveMemoryFilter:
    """
    敏感信息过滤器:在记忆写入前检测敏感内容。
    
    检测模式:
    - 密码/密钥:password, secret, api_key, token
    - 身份信息:身份证号、手机号、银行卡号
    - 企业机密:营收、利润、未公开战略
    """
    
    # 敏感关键词模式
    SENSITIVE_PATTERNS = {
        "password": [
            r"password\s*[:=]\s*\S+",
            r"pwd\s*[:=]\s*\S+",
            r"密码\s*[:=]\s*\S+",
        ],
        "secret_key": [
            r"secret\s*[:=]\s*\S+",
            r"api_key\s*[:=]\s*\S+",
            r"access_token\s*[:=]\s*\S+",
            r"sk-[a-zA-Z0-9]{48}",  # OpenAI API Key格式
        ],
        "identity": [
            r"\d{18}",  # 身份证号
            r"1[3-9]\d{9}",  # 手机号
            r"\d{16,19}",  # 银行卡号
        ],
        "financial": [
            r"营收\d+\.?\d*\s*[万亿]?",
            r"利润\d+\.?\d*\s*[万亿]?",
            r"毛利率\s*\d+\.?\d*%",
        ]
    }
    
    def check(self, content: str, metadata: Dict = None) -> Tuple[bool, SensitivityLevel, str]:
        """
        检查内容是否包含敏感信息。
        
        Returns:
            (is_sensitive, level, reason)
        """
        for category, patterns in self.SENSITIVE_PATTERNS.items():
            for pattern in patterns:
                if re.search(pattern, content, re.IGNORECASE):
                    # 根据类别判断敏感度
                    if category in ["password", "secret_key"]:
                        return True, SensitivityLevel.RESTRICTED, f"检测到{category}: {pattern}"
                    elif category == "identity":
                        return True, SensitivityLevel.CONFIDENTIAL, f"检测到身份信息: {pattern}"
                    else:
                        return True, SensitivityLevel.INTERNAL, f"检测到{category}: {pattern}"
        
        return False, SensitivityLevel.PUBLIC, "无敏感信息"
    
    def sanitize(self, content: str, level: SensitivityLevel) -> str:
        """
        对敏感内容进行脱敏处理。
        
        RESTRICTED: 完全删除,返回空
        CONFIDENTIAL: 替换为占位符
        INTERNAL: 标记为敏感但保留
        """
        if level == SensitivityLevel.RESTRICTED:
            # 完全拒绝存储,返回空
            return ""
        
        if level == SensitivityLevel.CONFIDENTIAL:
            # 替换敏感信息为占位符
            sanitized = content
            sanitized = re.sub(r"\d{18}", "[身份证号已隐藏]", sanitized)
            sanitized = re.sub(r"1[3-9]\d{9}", "[手机号已隐藏]", sanitized)
            sanitized = re.sub(r"\d{16,19}", "[银行卡号已隐藏]", sanitized)
            return sanitized
        
        # INTERNAL: 添加标记但保留内容
        return f"[敏感信息-内部级别] {content}"


# ========== 使用示例 ==========
if __name__ == "__main__":
    filter = SensitiveMemoryFilter()
    
    # 场景1:密码信息
    is_sensitive, level, reason = filter.check("用户密码: MyP@ssw0rd123")
    print(f"密码检测: 敏感={is_sensitive}, 级别={level.value}, 原因={reason}")
    print(f"  脱敏后: '{filter.sanitize('用户密码: MyP@ssw0rd123', level)}'")
    
    # 场景2:身份证号
    is_sensitive, level, reason = filter.check("用户身份证号: 110101199001011234")
    print(f"身份证检测: 敏感={is_sensitive}, 级别={level.value}")
    print(f"  脱敏后: '{filter.sanitize('用户身份证号: 110101199001011234', level)}'")
    
    # 场景3:正常信息
    is_sensitive, level, reason = filter.check("用户喜欢深色模式")
    print(f"正常检测: 敏感={is_sensitive}, 级别={level.value}")

⚠️ 生产环境风险:基于正则的敏感检测无法覆盖所有场景(如Base64编码、分块传输)。建议生产环境补充:① NLP敏感分类模型 ② 加密字段标记(用户手动标记) ③ 定期审计日志。

验证步骤

bash 复制代码
python -c "
from sensitive_memory_filter import SensitiveMemoryFilter
f = SensitiveMemoryFilter()
assert f.check('用户密码: 123')[0] == True, '密码检测失败'
assert f.check('用户喜欢深色模式')[0] == False, '正常内容误杀'
print('Layer 1 验证通过')
"

五、Layer 2:重要性评分------常用记忆保留,冷门记忆淘汰

目标:给每条记忆打一个"重要性分数",分数低的定期淘汰。

评分维度

  1. 访问频率:被检索/引用的次数越多,越重要
  2. 显式标记:用户主动标记"重要"的记忆,基础分高
  3. 关联度:与其他重要记忆有关联的,分数会传染
python 复制代码
from typing import Optional
from dataclasses import dataclass, field
from datetime import datetime
import math

@dataclass
class MemoryItem:
    """带遗忘机制的记忆项。"""
    id: str
    content: str
    created_at: datetime = field(default_factory=datetime.now)
    access_count: int = 0
    last_accessed: Optional[datetime] = None
    importance_score: float = 0.5  # 0-1,初始0.5
    explicit_mark: bool = False    # 用户是否显式标记重要
    sensitivity: SensitivityLevel = SensitivityLevel.PUBLIC

class ImportanceScorer:
    """
    重要性评分器:基于访问频率、显式标记、关联度计算记忆重要性。
    """
    
    def __init__(self):
        self.access_weight = 0.4
        self.mark_weight = 0.3
        self.recency_weight = 0.3
    
    def score(self, memory: MemoryItem) -> float:
        """
        计算记忆的重要性分数(0-1)。
        
        公式:
        score = access_score * 0.4 + mark_score * 0.3 + recency_score * 0.3
        """
        # 1. 访问频率分数(对数压缩,防止极端值)
        access_score = min(math.log10(memory.access_count + 1) / 3, 1.0)
        
        # 2. 显式标记分数
        mark_score = 1.0 if memory.explicit_mark else 0.0
        
        # 3. 时间衰减分数(越近访问越重要)
        if memory.last_accessed:
            days_since = (datetime.now() - memory.last_accessed).days
            recency_score = math.exp(-days_since / 30)  # 30天衰减到1/e
        else:
            recency_score = 0.1  # 从未被访问过
        
        # 综合评分
        total_score = (
            access_score * self.access_weight +
            mark_score * self.mark_weight +
            recency_score * self.recency_weight
        )
        
        return min(total_score, 1.0)
    
    def mark_important(self, memory: MemoryItem):
        """用户显式标记记忆为重要。"""
        memory.explicit_mark = True
        memory.importance_score = self.score(memory)
    
    def record_access(self, memory: MemoryItem):
        """记录一次访问,更新分数。"""
        memory.access_count += 1
        memory.last_accessed = datetime.now()
        memory.importance_score = self.score(memory)
    
    def get_eviction_candidates(self, memories: List[MemoryItem], 
                                 threshold: float = 0.3, 
                                 max_candidates: int = 10) -> List[MemoryItem]:
        """
        获取应该被淘汰的低分记忆。
        
        Args:
            threshold: 重要性分数低于此值的记忆被淘汰
            max_candidates: 每次最多淘汰多少条
        """
        # 重新计算所有分数
        for memory in memories:
            memory.importance_score = self.score(memory)
        
        # 筛选低分记忆
        candidates = [
            m for m in memories 
            if m.importance_score < threshold and not m.explicit_mark
        ]
        
        # 按分数排序,淘汰最低分的
        candidates.sort(key=lambda m: m.importance_score)
        return candidates[:max_candidates]


# ========== 使用示例 ==========
if __name__ == "__main__":
    scorer = ImportanceScorer()
    
    # 创建模拟记忆
    memories = [
        MemoryItem(id="1", content="用户喜欢深色模式", access_count=50, last_accessed=datetime.now()),
        MemoryItem(id="2", content="用户去年用过的一次性配置", access_count=1, last_accessed=datetime(2025, 1, 1)),
        MemoryItem(id="3", content="用户显式标记的重要项目规范", access_count=5, explicit_mark=True),
        MemoryItem(id="4", content="用户上周问过的一个边缘问题", access_count=2, last_accessed=datetime(2026, 6, 20)),
    ]
    
    # 计算分数
    print("=== 记忆重要性评分 ===")
    for memory in memories:
        score = scorer.score(memory)
        print(f"[{score:.2f}] {memory.content[:30]}... (访问{memory.access_count}次)")
    
    # 获取淘汰候选
    candidates = scorer.get_eviction_candidates(memories, threshold=0.3)
    print(f"\n=== 建议淘汰的记忆 ===")
    for c in candidates:
        print(f"[{c.importance_score:.2f}] {c.content}")

⚠️ 使用风险:分数阈值(threshold=0.3)需根据业务场景调整。设置过高→误删重要记忆;设置过低→淘汰不力。建议:首次设为0.3,运行一周后根据"被误删投诉率"调整。

验证步骤

bash 复制代码
python -c "
from importance_scorer import ImportanceScorer, MemoryItem
from datetime import datetime
s = ImportanceScorer()
m = MemoryItem(id='t', content='test', access_count=10, last_accessed=datetime.now())
assert s.score(m) > 0.3, '高频访问记忆分数应高于0.3'
m2 = MemoryItem(id='t2', content='test2', access_count=0, last_accessed=datetime(2020,1,1))
assert s.score(m2) < 0.3, '低频过期记忆分数应低于0.3'
print('Layer 2 验证通过')
"

六、Layer 3:时间衰减------旧记忆自动"褪色"

目标:让记忆像人脑一样"新学的东西记得牢,旧知识逐渐模糊"。

衰减函数选择

衰减模型 公式 特点 适用场景
指数衰减 score = initial * e^(-t/λ) 平滑连续,自然遗忘 一般记忆
阶梯衰减 score = initial * (1 - 0.1 * t/7) 每周降一档,可控 业务规则
幂律衰减 score = initial * t^(-α) 前期快、后期慢 热点信息
阈值淘汰 t > TTL → 直接删除 简单直接 临时数据
python 复制代码
import math
from datetime import datetime, timedelta

class TimeDecayManager:
    """
    时间衰减管理器:基于遗忘曲线对记忆进行降权。
    """
    
    def __init__(self, half_life_days: float = 30.0):
        """
        half_life_days: 半衰期(分数降到初始值一半所需天数)
        """
        self.half_life = half_life_days
        self.decay_constant = math.log(2) / half_life_days
    
    def calculate_decay(self, memory: MemoryItem, current_time: datetime = None) -> float:
        """
        计算记忆的时间衰减系数。
        
        返回0-1之间的值:1表示未衰减,0表示完全遗忘。
        """
        if current_time is None:
            current_time = datetime.now()
        
        age_days = (current_time - memory.created_at).days
        
        # 指数衰减
        decay_factor = math.exp(-self.decay_constant * age_days)
        
        # 如果记忆被频繁访问,衰减变慢(每次访问重置半衰期)
        if memory.last_accessed:
            days_since_access = (current_time - memory.last_accessed).days
            recency_boost = math.exp(-days_since_access / 7)  # 7天内访问过的记忆不衰减
            decay_factor = max(decay_factor, recency_boost * 0.5)
        
        return decay_factor
    
    def apply_decay(self, memory: MemoryItem, current_time: datetime = None) -> float:
        """
        应用时间衰减到记忆的重要性分数。
        
        返回衰减后的分数。
        """
        decay_factor = self.calculate_decay(memory, current_time)
        decayed_score = memory.importance_score * decay_factor
        return min(decayed_score, 1.0)
    
    def should_forget(self, memory: MemoryItem, 
                     threshold: float = 0.1,
                     current_time: datetime = None) -> bool:
        """
        判断记忆是否应该被"遗忘"(淘汰)。
        
        标准:衰减后的分数低于阈值,且未被显式标记为重要。
        """
        if memory.explicit_mark:
            return False  # 显式标记的记忆不遗忘
        
        decayed_score = self.apply_decay(memory, current_time)
        return decayed_score < threshold


# ========== 使用示例:艾宾浩斯遗忘曲线 ==========
if __name__ == "__main__":
    decay_manager = TimeDecayManager(half_life_days=30)
    
    # 模拟一条记忆随时间的衰减
    memory = MemoryItem(
        id="test",
        content="用户的重要项目规范",
        created_at=datetime(2026, 1, 1),
        importance_score=0.8,
        access_count=0
    )
    
    print("=== 记忆时间衰减模拟 ===")
    for month in range(1, 13):
        current = datetime(2026, month, 1)
        decay_factor = decay_manager.calculate_decay(memory, current)
        decayed_score = decay_manager.apply_decay(memory, current)
        should_forget = decay_manager.should_forget(memory, threshold=0.1, current_time=current)
        
        status = "🗑️ 遗忘" if should_forget else "📌 保留"
        print(f"{current.strftime('%Y-%m')}: 衰减系数={decay_factor:.3f}, 分数={decayed_score:.3f} {status}")
    
    # 对比:如果中途访问一次,衰减变慢
    print("\n=== 中途访问后的衰减 ===")
    memory.access_count = 1
    memory.last_accessed = datetime(2026, 3, 15)  # 3月访问过一次
    
    for month in [6, 9, 12]:
        current = datetime(2026, month, 1)
        decayed_score = decay_manager.apply_decay(memory, current)
        print(f"{current.strftime('%Y-%m')}: 分数={decayed_score:.3f} (因3月访问过,衰减变慢)")

⚠️ 半衰期调优:half_life_days=30 适用于一般业务场景。高频业务(如客服对话)建议7天;低频业务(如年度规范)建议90天。半衰期过短会导致"学到就忘",建议结合A/B测试确定最优值。

验证步骤

bash 复制代码
python -c "
from time_decay_manager import TimeDecayManager, MemoryItem
from datetime import datetime
dm = TimeDecayManager(half_life_days=30)
fresh = MemoryItem(id='f', content='fresh', created_at=datetime.now(), importance_score=0.8)
old = MemoryItem(id='o', content='old', created_at=datetime(2024,1,1), importance_score=0.8)
assert dm.apply_decay(fresh) > 0.7, '新记忆不应大幅衰减'
assert dm.apply_decay(old) < 0.1, '旧记忆应大幅衰减'
print('Layer 3 验证通过')
"

七、整合三层:智能遗忘管理器

python 复制代码
class SmartMemoryManager:
    """
    智能记忆管理器:整合敏感过滤、重要性评分、时间衰减三层遗忘机制。
    """
    
    def __init__(self, half_life_days: float = 30.0, 
                 importance_threshold: float = 0.3,
                 max_memory_size: int = 1000):
        self.memories: Dict[str, MemoryItem] = {}
        self.sensitive_filter = SensitiveMemoryFilter()
        self.importance_scorer = ImportanceScorer()
        self.decay_manager = TimeDecayManager(half_life_days)
        self.importance_threshold = importance_threshold
        self.max_memory_size = max_memory_size
    
    def store(self, content: str, memory_id: str = None, 
              explicit_mark: bool = False,
              metadata: Dict = None) -> Optional[MemoryItem]:
        """
        存储记忆(带三层遗忘检查)。
        
        Returns:
            MemoryItem: 成功存储
            None: 被敏感过滤拦截
        """
        # Layer 1: 敏感过滤
        is_sensitive, level, reason = self.sensitive_filter.check(content)
        
        if level == SensitivityLevel.RESTRICTED:
            print(f"[遗忘-敏感拦截] 记忆被拒绝: {reason}")
            return None
        
        # 脱敏处理
        content = self.sensitive_filter.sanitize(content, level)
        
        # 创建记忆项
        memory = MemoryItem(
            id=memory_id or f"mem_{len(self.memories)}",
            content=content,
            explicit_mark=explicit_mark,
            sensitivity=level
        )
        
        # Layer 2: 初始重要性评分
        memory.importance_score = self.importance_scorer.score(memory)
        
        self.memories[memory.id] = memory
        print(f"[存储] {memory.id}: 分数={memory.importance_score:.2f}, 敏感级别={level.value}")
        
        # 检查是否需要触发清理
        if len(self.memories) > self.max_memory_size:
            self.cleanup()
        
        return memory
    
    def recall(self, query: str) -> List[MemoryItem]:
        """
        检索记忆(同时更新访问记录和分数)。
        
        简化版:基于关键词匹配(实际应用向量检索)。
        """
        # 1. 先清理过期记忆
        self._apply_decay_to_all()
        
        # 2. 检索(简化:关键词匹配)
        results = []
        for memory in self.memories.values():
            if query.lower() in memory.content.lower():
                # 更新访问记录
                self.importance_scorer.record_access(memory)
                results.append(memory)
        
        # 按重要性分数排序
        results.sort(key=lambda m: m.importance_score, reverse=True)
        return results
    
    def cleanup(self):
        """定期清理:淘汰低分+过期的记忆。"""
        print(f"\n[清理] 当前记忆数: {len(self.memories)}")
        
        # 1. 应用时间衰减
        self._apply_decay_to_all()
        
        # 2. 获取淘汰候选
        candidates = self.importance_scorer.get_eviction_candidates(
            list(self.memories.values()),
            threshold=self.importance_threshold
        )
        
        # 3. 执行淘汰
        removed = 0
        for candidate in candidates:
            if candidate.id in self.memories:
                del self.memories[candidate.id]
                removed += 1
        
        print(f"[清理] 淘汰 {removed} 条记忆,剩余 {len(self.memories)} 条")
    
    def _apply_decay_to_all(self):
        """对所有记忆应用时间衰减。"""
        for memory in self.memories.values():
            decayed_score = self.decay_manager.apply_decay(memory)
            memory.importance_score = decayed_score
    
    def get_memory_stats(self) -> Dict:
        """获取记忆库统计信息。"""
        if not self.memories:
            return {"total": 0}
        
        scores = [m.importance_score for m in self.memories.values()]
        return {
            "total": len(self.memories),
            "avg_score": sum(scores) / len(scores),
            "min_score": min(scores),
            "max_score": max(scores),
            "restricted_count": sum(1 for m in self.memories.values() if m.sensitivity == SensitivityLevel.RESTRICTED)
        }


# ========== 使用示例:完整流程 ==========
if __name__ == "__main__":
    manager = SmartMemoryManager(half_life_days=30, max_memory_size=5)
    
    print("=== 存储记忆 ===")
    
    # 1. 正常记忆
    manager.store("用户喜欢深色模式", memory_id="pref_1", explicit_mark=True)
    manager.store("用户对响应速度要求高")
    
    # 2. 敏感记忆(应被拦截或脱敏)
    manager.store("用户密码: MyP@ssw0rd123")
    manager.store("用户身份证号: 110101199001011234")
    
    # 3. 更多记忆(触发清理)
    manager.store("用户去年用过的一次性配置", memory_id="old_1")
    manager.store("用户偶尔提到的一个边缘功能")
    
    print("\n=== 检索记忆 ===")
    results = manager.recall("用户")
    for r in results[:3]:
        print(f"[{r.importance_score:.2f}] {r.content[:40]}...")
    
    print("\n=== 统计 ===")
    stats = manager.get_memory_stats()
    print(f"记忆统计: {stats}")

⚠️ 集成风险:SmartMemoryManager 的 recall() 使用了简化关键词匹配。生产环境替换为向量检索(如 Chroma/FAISS)时,需注意:① 遗忘操作需同步更新向量索引 ② 敏感过滤应在向量化前执行 ③ 清理后索引需重建

验证步骤

bash 复制代码
python -c "
from smart_memory_manager import SmartMemoryManager
m = SmartMemoryManager(max_memory_size=3)
m.store('test1'); m.store('test2'); m.store('test3')
# 触发清理
m.store('test4')
stats = m.get_memory_stats()
assert stats['total'] <= 3, '超出最大容量限制'
print(f'整合验证通过: 当前{stats[\"total\"]}条记忆')
"

八、与Loop Engineering和内容矩阵的关联

8.1 与Loop Engineering的关联

Loop Engineering中的**Hill Climbing Loop(第四层)**核心理念是"Agent越用越聪明"。遗忘机制是Hill Climbing的前提------如果Agent记住的全是垃圾信息,它越用越糊涂。

Loop层级 与遗忘机制的关系
Level 1 Agent Loop 每次循环结束后,哪些中间状态该记住、哪些该遗忘?
Level 2 Verification Loop 验证失败的尝试,记忆还是遗忘?(建议:短期记住避免重复犯错,长期遗忘)
Level 3 Event Driven Loop 定时触发清理任务,淘汰过期记忆
Level 4 Hill Climbing Loop 基于使用反馈优化记忆策略(哪些记忆被频繁访问?哪些从未被访问?)

8.2 与已有内容矩阵的闭环

复制代码
Agent Memory架构选型(选型指南)------ 已有 ✅
    ↓ 向量+图谱+关系数据库的选择
OpenSPG构建Agent记忆(知识图谱实战)------ 已有 ✅
    ↓ 知识图谱作为记忆存储层
本文:Agent记忆遗忘机制 ------ 新增 ✅
    ↓ 记忆需要"会忘"才算智能
Loop Engineering(循环控制)------ 已有 ✅
    ↓ 循环中的状态持久化与遗忘策略
GB/Z 185(标准合规)------ 已有 ✅
    ↓ 敏感信息过滤对应6.2数据隐私条款

九、总结

本文是一篇讨论Agent记忆遗忘机制与实现的文章,核心交付:

层级 机制 核心代码 解决什么问题
Layer 1 敏感过滤 SensitiveMemoryFilter 密码/隐私不进记忆库
Layer 2 重要性评分 ImportanceScorer 常用记忆保留,冷门记忆淘汰
Layer 3 时间衰减 TimeDecayManager 旧记忆自动"褪色"
整合 智能管理器 SmartMemoryManager 三层机制协同工作

核心结论

  1. Agent Memory不是"记住越多越好",不会遗忘的Agent是危险的
  2. 三层遗忘机制缺一不可:敏感过滤保安全、重要性评分保质量、时间衰减保鲜度
  3. 遗忘机制是Hill Climbing Loop的前提:只有定期清理垃圾记忆,Agent才能越用越聪明
  4. GB/Z 185 6.2数据隐私条款要求敏感信息分级存储,遗忘机制的第一层直接对应此要求

十、方案对比与选型建议

10.1 三种衰减模型对比

维度 指数衰减 阶梯衰减 幂律衰减
公式 score * e^(-t/λ) score * (1-0.1*t/7) score * t^(-α)
遗忘曲线 平滑连续 阶梯下降 前期快后期慢
适用场景 一般记忆(推荐默认) 业务规则(按周过期) 热点事件(快速降温)
参数调优 half_life_days 控制速率 阶梯步长控制 α 控制衰减力度
实现复杂度 ⭐ 低 ⭐⭐ 中 ⭐⭐⭐ 高
推荐度 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐

10.2 三层遗忘机制选型指南

场景 必须启用 建议启用 可选启用
客服Agent Layer 1(敏感过滤) Layer 2(重要评分) Layer 3(时间衰减)
代码开发Agent Layer 1(API Key过滤) Layer 2 + Layer 3 -
企业知识库Agent Layer 1(机密信息过滤) Layer 3(版本过期) Layer 2
个人助手Agent Layer 1 + Layer 2 Layer 3 -

10.3 适用边界说明

⚠️ Layer 1 不适用场景:纯公开数据集(如维基百科检索)、无需隐私保护的测试环境------正则检测会增加写入延迟

⚠️ Layer 2 不适用场景:记忆总量<100条(淘汰无意义)、所有记忆同等重要的场景(如配置项集合)

⚠️ Layer 3 不适用场景:永久有效的记忆(如法律条款、公司制度)、需要精确时间点回溯的场景


相关阅读:


你的Agent现在有遗忘机制吗? 是无限增长(hoarder),还是定期清理?评论区说说你的策略------如果记忆库已经膨胀了,是怎么处理的?我针对高频场景整理一份"Agent Memory清理脚本",直接复制就能跑。

收藏这篇Agent遗忘机制指南,设计Agent记忆系统时别忘了"会忘"比"会记"更重要。觉得有用的话点赞+收藏,收藏率决定算法推荐权重,让更多开发者看到这篇Agent记忆安全的完整方案。


📅 更新日志

日期 更新内容
2026-07 初始发布(基于Python 3.10+ / 标准库)

⚠️ 版本变更提示 :本文基于Python 3.10+ 标准库(re/math/datetime)实现。如果Python大版本升级(如3.14+),datetime.now() 仍保持兼容。三层遗忘策略的逻辑与编程语言无关,可直接移植到TypeScript/Go/Java。

相关推荐
新知图书3 小时前
向量数据库集成(Chroma/Pinecone/Milvus)
人工智能·agent·多模态·ai agent·智能体
love530love18 小时前
将 ChatCut MCP 插件从 Codex 桌面应用移植到 WorkBuddy —— 完整适配实录
ide·人工智能·windows·视频剪辑·ai agent
新知图书1 天前
情感能力相关的开源软件与工具:实践形态与代表项目
人工智能·agent·ai agent·智能体
带娃的IT创业者1 天前
从 iptv-org/iptv 看开源协作:如何像 AI Agent 一样思考工程化实践
人工智能·开源·github·软件开发·ai agent·工程化实践·开源协作
新知图书2 天前
记忆能力的定义与内涵
人工智能·agent·ai agent·智能体
大厂AI实战3 天前
Token 节约实战:全链路降本 90%+
token·ai agent·ai 编程
仙逆GPT3 天前
GPT-5.6三模型怎么选?Sol、Terra和Luna工程应用对比
luna·ai agent·sol·gpt-5.6·terra
新知图书3 天前
文档感知任务定义与分类
人工智能·agent·ai agent·智能体
森叶4 天前
从 MCP 到扣子:Agent 平台化演化路径
人工智能·大模型·agent·ai agent·mcp