ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

给 AI Agent 修了一个“记忆断层”:从先删后写到安全迁移

给 AI Agent 修了一个“记忆断层”:从先删后写到安全迁移 最近整理自己的智能运维 Agent 项目时我发现三层记忆系统里有一个比“摘要写得好不好”更基础的问题旧对话先被删掉新的摘要却还没生成。这次没有重写记忆架构而是先调整迁移顺序目标层写入成功再清理源数据。让异步失败留下可重试的原料而不是留下一个上下文缺口。本文要点异步可以缩短等待但不会自动保证数据安全。先写后删允许短暂重复去重和幂等负责收尾。当前先做轻量修复持久化任务和更完整的恢复机制留待后续。一、三层记忆为什么会有“断层”项目把聊天记忆分成三层L1 保存近期原始对话L2 保存较早对话的摘要L3 通过 pgvector 保存可检索的历史记忆。L1、L2 在进程内保留热缓存并写入 Redis摘要与归档放到后台异步执行。原来的顺序大致是删除 L1 旧对话 → 提交摘要任务 → 生成摘要 → 写入 L2问题出在中间那段空窗模型调用失败、Redis 写入异常甚至线程池拒绝任务都可能让源记忆已经消失、目标记忆却没有落地。L2 向 L3 归档也有类似风险。这里说的“丢失”主要是模型可用上下文出现缺口。正常认证对话的完整原文另由 PostgreSQL 记录但它不会因为摘要失败就自动回填到记忆链路。二、修改思路把删除放到成功之后核心规则只有一句话没有确认目标写入成功就不删除源记忆。保留源记忆 → 异步生成/归档 → 目标写入成功 → 清理源记忆摘要失败原文留在 L1向量化或数据库写入失败摘要留在 L2。任务没有成功交出去也按失败处理不让“提交了异步任务”被误当成“迁移完成”。还有一个容易忽略的情况目标已经写成功源数据却没删掉。这时允许两份暂时并存。L2 已经生成时后续只补做源清理不再重复调用摘要模型。我的取舍是暂时多留一份比提前删掉更容易补救。但这个取舍必须配合去重不能让同一段记忆反复进入 Prompt。三、不是只交换两行代码源数据保留后同一条记忆可能再次被扫描到所以还要控制重复任务。这次用进程内的处理中 ID 集合避免同一条记忆被反复派发目标写入使用稳定 ID让重复执行尽量落到同一条记录拼接上下文时按 ID 去重重叠时优先使用原文。异步任务也可能乱序完成因此摘要要按对话轮次排序。旧会话的回调不能修改后来重新建立的同名会话。失败后的重试并没有做成复杂调度系统后续新增对话、或者应用启动恢复时会根据仍保留的源数据重新尝试。它不是定时重试也不是跨实例的任务协调。四、为什么这次没有直接上任务队列当前项目更需要一套能解释、能验证、能继续迭代的方案。已有 Spring 异步执行、Redis 和 PostgreSQL先沿着现有结构修复改动范围更容易控制。本次没有新增中间件、依赖或数据库表因此没有额外的消息队列部署负担也不需要同步调整大量业务接口。代价仍然存在持续失败会让旧记忆暂时超过容量增加上下文和 Token 开销重试也可能再次产生模型费用。用户不用为每次摘要同步等待但后台积压不能长期放任不管。如果以后转向多实例或更高可靠性要求再加入持久化任务、退避重试、失败告警以及从原文数据库重建记忆的补偿入口会更合理。五、验证结果以及这次没有解决的事这次新增了 34 条测试覆盖摘要失败、目标写入失败、源清理失败、任务拒绝、恢复重试和重复上下文等情况。全项目 103 项测试通过应用也在 Java 17 的 Docker 环境中成功启动。模型及部分存储故障通过 Mock 验证项目的 pgvector 集成测试使用真实容器。本轮没有进行真实模型长对话或多实例压力验收所以不能据此声称“生产环境绝不丢数据”。Redis 初始写入失败、过期或淘汰后的自动回填仍需要后续治理。正在运行的归档任务与清空会话之间的彻底协调也没有在这次最小修复中全部解决。结语这次优化给我最大的提醒是异步任务不只是“另开一个线程”它还需要明确的成功边界。先保留什么、什么时候能删、失败后靠什么重试这些问题比多加一层存储更基础。对当前项目来说先把这个顺序修对比一次把所有可靠性机制堆齐更有价值。实现与验证记录GitHub 修复提交。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进