云计算百科
云计算领域专业知识百科平台

Agent 记忆会“满“吗?重要性×时效性×白名单,给记忆排个淘汰优先级

Agent 记忆会"满"吗?重要性×时效性×白名单,给记忆排个淘汰优先级

本文是「Agent 应用开发工程师」系列第⑤篇 · 上下文是预算,缓存是第一刀,记忆淘汰是第二刀——把最值得记住的留下。


⚡ TL;DR · 先看结论

  • Agent 记忆不是无限仓库,上下文窗口满了就得淘汰,淘汰不能靠"先进先出"——重要记忆刚说完就被扔了。
  • 本文给出一个三维评分公式:score = importance × recency × whitelist,用 Redis 有序集合(Sorted Set)做优先级淘汰。
  • 白名单机制保护关键记忆不被误杀;TTL + 冷热分层防止"刚记住就被扔"。
  • 所有权重/阈值都是骨架,得你自己按场景调参,别写死"最佳值"。

前两篇我们讲了"预算"(Context Engineering)和"缓存"(Prompt Caching)。这一篇接着讲预算的另一半——记忆淘汰。

Cache 管的是"系统提示和工具定义反复算",Memory 管的是"用户说了什么、Agent 发现过什么"。但窗口就那么大,满了之后扔什么,比"用什么方式存"更值得想清楚。

这篇不讲"怎么用向量库存住所有记忆"——那是存储层的事。我们讲满了之后怎么扔:三维评分公式、Redis 排序集实现、白名单保护、冷热分层,以及调参不该踩的坑。


一、先立标准:为什么"先进先出"不适合 Agent

先进先出(FIFO)是最简单的淘汰策略——谁先来谁先走。但在 Agent 场景里,它大概率会做错事:

  • 用户刚说了一句"我的项目密码是 XXXX",FIFO 可能因为前面排了太多轮对话,直接把它挤出去。下次 Agent 就忘了密码。
  • Agent 刚发现了一个关键工具调用结果,FIFO 可能因为这条消息在窗口里位置靠前,优先淘汰它。

所以 Agent 记忆淘汰需要按"价值"排优先级,而不是按时间顺序。

💡 一个反直觉的事实:长对话里,最重要的信息往往出现在中间——既不是最早(开场寒暄),也不是最晚(当前问题)。FIFO 和 LRU 在这种场景下都不好用。


二、三维评分公式:重要性 × 时效性 × 白名单

一个可落地的方案是给每条记忆打一个综合分,按分淘汰——分最低的先走。

score = importance × recency_weight × whitelist_boost

维度含义评分思路
重要性 (importance) 这条记忆"值多少钱" 用户明确标注的(“记住这个”)、工具返回的错误/告警、涉及安全/配置的信息 → 高分;日常闲聊 → 低分
时效性 (recency) 这条记忆"多新鲜" 刚发生的 → 分高;半小时前的 → 分逐渐降低。可用指数衰减 weight = e^(-λ·Δt)
白名单 (whitelist) 这条记忆"能不能碰" 白名单内的 → 乘一个大系数(如 10),永不淘汰;不在白名单 → 乘 1

⚖️ 权重不说死:λ(衰减系数)、importance 分档、whitelist 乘数——全都得按你的场景调。正文给公式和思路,不给"最佳值"。

重要性分档(示例,非固定)

类别分说明
用户明确指令/配置 10 “记住这个密码”“把这条规则记下来”
工具返回错误/告警 8 API 调用失败、权限不足
关键业务数据 7 订单号、项目 ID、配置项
Agent 发现的重要结论 5 经过推理得出的事实
一般对话上下文 3 日常问答
问候/闲聊 1 开场白、寒暄

三、用 Redis 有序集合做优先级淘汰

Redis 的 Sorted Set(有序集合) 天然适合这个场景:ZADD 把记忆 ID 和 score 写进去,ZRANK 查排名,ZREMRANGEBYSCORE 按分淘汰——不存在"遍历所有记忆找最不重要的"。

# ⚠️ 骨架示例:Redis 排序集做记忆淘汰,以你实际 Redis 版本为准
import redis, time, json

r = redis.Redis(host="localhost", port=6379, db=0)
MEMORY_KEY = "agent:memory:priority"

def remember(memory_id: str, importance: float, is_whitelisted: bool = False):
"""写入一条记忆及其评分"""
recency = 1.0 # 刚发生的,时效性满分
whitelist = 10.0 if is_whitelisted else 1.0
score = importance * recency * whitelist
r.zadd(MEMORY_KEY, {memory_id: score})

def decay_memories(decay_rate: float = 0.95):
"""每隔一段时间给所有记忆的时效性乘一个衰减系数"""
for mem_id in r.zrange(MEMORY_KEY, 0, 1):
mem_id = mem_id.decode()
current = r.zscore(MEMORY_KEY, mem_id)
# 白名单记忆不衰减
if current and current >= 10.0:
continue
# 非白名单:时效性衰减
new_score = current * decay_rate if current else 0
r.zadd(MEMORY_KEY, {mem_id: new_score})

def evict_if_full(max_size: int = 100, evict_count: int = 10):
"""如果记忆超过上限,淘汰评分最低的 evict_count 条"""
current = r.zcard(MEMORY_KEY)
if current > max_size:
# 淘汰分最低的
to_evict = current max_size + evict_count
# 找到最低分的记忆
lowest = r.zrange(MEMORY_KEY, 0, to_evict 1)
# 删除它们
for mem_id in lowest:
r.zrem(MEMORY_KEY, mem_id)
print(f"Evicted {len(lowest)} memories")

🧭 为什么用 Sorted Set 而不是 List:List 只能按插入顺序"先进先出",Sorted Set 可以按分排序,取最低分、按范围删除都是 O(log N)——这在记忆量大的时候是质的区别。

三个要命的边界

  • 别让白名单膨胀:白名单记忆不会被淘汰,不加控制就会把窗口塞满。建议设白名单上限(比如 20 条),超限时按 importance × recency 在白名单内再排一次。
  • 衰减需要定时触发:decay_memories 不是自动的。可以放在每次 remember() 调用时顺带执行,或者用一个定时任务(如 schedule 库)周期性触发。
  • 分数碰撞:如果多条记忆分数相同,ZREMRANGEBYSCORE 会全删。建议 score 加一个微小的随机扰动(importance * recency * whitelist + random() * 0.001)避免同分批量误杀。

  • 四、白名单机制:有些记忆不能被淘汰

    白名单是关键设计。不是所有记忆都该参与淘汰——有些是"死了也不能丢"的:

    • 用户身份信息(“我是张三”)
    • 系统配置/偏好(“用中文回复”)
    • 安全凭证(“我的 API key 在环境变量里”)
    • 长任务上下文(“我们正在处理订单 #12345”)

    实现上,这些记忆的 score 被乘一个大系数(如 10),让它们永远排在 Sorted Set 的顶端。

    # ⚠️ 骨架示例:白名单保护逻辑
    WHITELIST_BOOST = 10.0

    def remember_with_whitelist(memory_id: str, importance: float,
    is_whitelisted: bool = False):
    whitelist = WHITELIST_BOOST if is_whitelisted else 1.0
    recency = 1.0
    score = importance * recency * whitelist

    # 如果是白名单,记录白名单元数据备用
    if is_whitelisted:
    r.hset("agent:memory:whitelist", memory_id, json.dumps({
    "importance": importance,
    "created_at": time.time()
    }))

    r.zadd(MEMORY_KEY, {memory_id: score})

    ⚠️ 白名单的"白"不是永久的:用户可能改主意(“不再用中文”)、任务可能结束(订单已完成)。建议提供一个"降级"接口,把白名单记忆降为普通记忆,参与后续淘汰。


    五、TTL + 冷热分层:防止"刚记住就被扔"

    还有一个常见问题:一条记忆刚写进去,因为初始分数低(比如普通闲聊 importance=1),下一轮淘汰就被扔了。

    三个办法:

  • 新记忆保护期:新写入的记忆在 TTL 内(比如 30 秒)不参与淘汰。用 Redis 的 EXPIRE 或单独维护一个"保护期"集合。
  • 冷热分层:分两层——热层(当前窗口,活跃记忆)和冷层(存档,可回溯但不计入上下文)。淘汰只在热层做,冷层用单独的淘汰策略(如 LRU)。
  • 最低分兜底:给所有记忆设一个 score_min(比如 0.1),避免分数衰减到 0 后下一轮必然被删——但白名单不受此限。
  • # ⚠️ 骨架示例:新记忆保护期
    PROTECTION_TTL = 30 # 秒

    def remember_with_protection(memory_id: str, importance: float, ...):
    # … 正常写入逻辑 …
    # 在保护期集合中记录
    r.sadd("agent:memory:protected", memory_id)
    r.expire(f"agent:memory:protect:{memory_id}", PROTECTION_TTL)

    def evict_except_protected(max_size: int):
    """淘汰时跳过保护期内的记忆"""
    protected = r.smembers("agent:memory:protected")
    # 只淘汰不在保护期内的低分记忆
    ...


    六、和 Context Engineering、Prompt Caching 合起来看

    系列走到这里,三张"成本牌"已经齐了:

    策略管什么怎么做
    Context Engineering 往窗口里放哪些 token 预算分配,L0-L5 分层
    Prompt Caching 系统提示/工具定义少算 前缀缓存,断点 + 预热
    记忆淘汰 用户说了什么该留 三维评分,Redis Sorted Set 淘汰

    三者的关系:Context Engineering 决定"放什么",Prompt Caching 确保"放进去的少算",记忆淘汰确保**“放不下的扔得对”**。

    💡 对应试/面试的启发:当面试官问"Agent 长对话怎么处理",不要只答"用 RAG 存"——把这三张牌串起来,比只答一个工具名深得多。


    七、10 条面向客户/面试的表达红线

    别这么说该怎么说 / 错在哪
    「用向量库存所有记忆就行」 存是存储层,淘汰是策略层,存得下不等于窗口放得下
    「先进先出够用了」 Agent 对话里最重要的信息居中,FIFO 正好淘汰它
    「我的评分公式是通用的」 权重/阈值按场景调,没有万能参数
    「白名单记忆永不淘汰」 白名单会膨胀,需要上限和"降级"机制
    「Redis 做记忆淘汰很慢」 Sorted Set 的 ZRANK/ZREMRANGEBYSCORE 是 O(log N),够快
    「分数越高越重要」 分数高只说明现在排在前面,衰减会改变排名
    「衰减系数设一个就行」 不同场景(短期对话 vs 长期项目)衰减速度不同
    「淘汰了就不管了」 淘汰的记忆可以归档到冷层,需要时还能回溯
    「记忆管理不需要测试」 模拟长对话、注入关键信息、看它是否被误杀——必须测
    「我的记忆方案是生产级的」 如果没跑过超过窗口的对话,它只是骨架,别写成"已上线"

    八、一句话带走

    记忆不是无限仓库,淘汰是必然的决策;用三维评分排优先级,用 Redis 排序集落地,用白名单保护关键记忆——然后把一切数字标成"按场景调参"。


    下期预告:第⑥篇讲「Delta Channels」——LangGraph 的 checkpoint 从 O(N²) 降到 O(N) 的"实验性"功能,值得每个做复杂 Agent 的人关注。


    参考:Redis Sorted Set 文档(ZADD/ZRANK/ZREMRANGEBYSCORE);MemGPT/LangMem 官方文档(以发布时版本为准);本文评分公式为骨架,权重/阈值需按场景调参。


    本系列目录

    • ① Java 后端不做 Agent?Spring AI + MCP 实战(已发布)
    • ② MCP vs A2A:协议栈位置与选型决策树(已发布)
    • ③ 别再只会写 Prompt:Context Engineering 才是 Agent 的下一个范式(已发布)
    • ④ Prompt 缓存不是玄学:命中判定、断点与预热(已发布)
    • ⑤ 记忆淘汰策略:重要性 × 时效性 × 白名单(本文)
    • ⑥ LangGraph Delta Channels——下期预告
    • 安全与可信 → 成本与跨栈 → 未完待续
    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Agent 记忆会“满“吗?重要性×时效性×白名单,给记忆排个淘汰优先级
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!