AI Agent之记忆系统Memory

更多 AI 文章见《智周万物(AI集萃)》专栏

记忆系统(Memory System)是决定 Agent 能否进行复杂多轮对话、长任务编排以及个性化服务的核心组件。它解决了大模型上下文窗口有限、无法跨会话保留信息、不能积累经验的根本问题。

记忆系统

Agent 的核心痛点在于大语言模型(LLM)的无状态性和上下文窗口的局限性。

  • 无状态性:每次 API 调用都是独立的,模型天然会"失忆";多轮对话的实现,是把历史记录打包回传,这不是真正的记忆。
  • 上下文窗口有限:即便是 100K 乃至 1M Token 的超长窗口,也无法承载数年、无限轮次的对话历史。且窗口过长会引入噪音、增加成本和延迟。
  • 信息组织与检索:真正的记忆不只是全量存储,而是像人脑一样,有结构、可联想、会遗忘。

因此,一个完整的记忆系统,本质上是一个信息全生命周期管理系统,负责记忆的获取、存储、检索、更新与遗忘。为 Agent 提供了类似人类的记忆能力,使其能够:

  • 跨会话记住用户偏好和历史交互
  • 积累领域知识和执行经验
  • 执行长期任务并跟踪进度
  • 基于过往经验做出更好的决策

分层架构

记忆系统的本质是解决大模型有限的 Context Window(上下文窗口)与海量业务历史数据之间的矛盾。

参考计算机存储架构:

plain 复制代码
[ 计算机存储架构 ]                    [ Agent 记忆架构 ]

+-------------------------+         +-------------------------+
|     CPU 寄存器          |   ==>   |    会话记忆 (Session)   | -> 原始多轮对话,直接参与当前推理
+-------------------------+         +-------------------------+
|     L1 / L2 缓存 (RAM)  |   ==>   |    短期记忆 (Summary)   | -> LLM压缩后的摘要,常驻Prompt
+-------------------------+         +-------------------------+
|     外部硬盘 (SSD/DB)   |   ==>   |    长期记忆 (Vector DB)  | -> 亿级海量历史,需要时才去模糊检索
+-------------------------+         +-------------------------+

1. 会话记忆 (Session Memory)

  • 工程本质: 内存(或 Redis)中的一个严格保序的 List[Message] 队列。
  • 核心职责: 维持当前对话的连贯性。解决多轮对话中的代词指代(如:"把它删了",AI 需要通过会话记忆知道"它"是指上一轮讨论的那个文件)。
  • 生命周期: 仅存在于当前会话周期,且只保留最近的 N 轮(通常 3-5 轮)。
  • 工程约束:绝对不能被 LLM 修改或概括,必须保留原始的 user / assistant 文本内容。

2. 短期记忆 (Short-term Memory)

  • 工程本质: 由 LLM 异步提炼出来的结构化 Profile 标签或一小段文本(Summary)。
  • 核心职责: 当会话变长、老消息被挤出会话记忆(Session Memory)时,短期记忆负责承接这些被挤出消息的核心含金量,作为"前情提要"常驻在 System Prompt 中。
  • 生命周期: 随着当前 Session 的进行动态更新,会话结束时可选择性归档。

3. 长期记忆 (Long-term Memory)

  • 工程本质: 向量数据库(Vector DB)与传统关系型数据库(SQL)的混合体。
  • 核心职责: 实现跨会话(Cross-Session)的数据持久化。记住用户几个月前设定的偏好、公司规章制度、或者过去所有历史任务的最终结局。
  • 生命周期: 永久存储,直到用户显式触发擦除命令(符合 GDPR 隐私法规)。

权衡与取舍

在架构落地时,必须注意:

  • 会话记忆的"窗口抖动"问题:在长上下文(如代码)环境下,若SessionMemory 的限制设得太小(比如 2 轮),会因被强行塞入ShortTermSummary 中,而丢失对细节的感知;随着Context 成本大幅下降,SessionMemory 可增大(如,最近 8-10 轮),以给予充足的原始现场缓冲。
  • 长期记忆的"垃圾数据污染":避免把无意义向量扔进长期向量库,引入记忆噪音;
    • 引入 Gatekeeper(看门狗)机制:只有通过了轻量级敏感度/价值评估的对话(例如包含实体、错误码、用户明确偏好的句子),才投递给后台任务写入长期向量库。
  • 数据一致性与分布式锁:连续新消息的到来时,异步短期记忆压缩(Summary Chain)可能还在运行(未完成)。此时会读取到旧的 Summary,导致大模型出现了"记忆错乱"或回复重复。
    • 在 Redis 中针对 session_id 加分布式锁,或者将会话的读写流变为基于时间戳的单线程队列化处理(Queue-based Processing),确保记忆的序列化线性一致性。

核心流程

Agent 的记忆不是单一模块,而是一条包含"写入"和"读取"的完整管道:

  • 写入管道------从对话到记忆:把原始对话转化为可检索的记忆;需经过,感知与提取、转换与摘要(生成摘要、提取实体,或直接生成假设性问题和答案)、向量化与索引、结构化存储(事实性信息,主动更新到结构化存储或知识图谱中)。
  • 读取管道------按需检索:Agent 不会将全部记忆放入 Prompt,而是在需要时触发检索。

记忆无限增长会引入噪音,并推高检索成本。因此,记忆管理必须包含整理和遗忘机制。

  • 摘要与合并:
    • 定期对工作记忆中的旧对话进行递归摘要。
    • 将多条相关的原子记忆整合为一条高度概括的记忆。
  • 反思与提炼:定期对近期情景记忆进行高层次总结,提炼出新的洞察和用户画像,作为更高级别的长期记忆。
  • 主动遗忘:
    • 基于时间:如艾宾浩斯遗忘曲线,降低非活跃记忆的权重。
    • 基于重要程度:由 LLM 对记忆打分,清理低分记忆。
    • 基于一致性:当出现冲突信息,主动更新或标记旧记忆,保留更新后的版本。

整的 Agent 请求闭环中,三层记忆系统的调用与协作流程如下

样例代码

采用外观模式 (Facade Pattern),统一封装的三层记忆调度逻辑,与异步落盘解耦的伪实现。

python 复制代码
import logging
import time
from typing import List, Dict, Any, Optional
from pydantic import BaseModel, Field

# 初始化生产级日志
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s")
logger = logging.getLogger("IndustrialMemorySystem")

class ChatMessage(BaseModel):
    role: str = Field(..., pattern="^(user|assistant|system)$")
    content: str = Field(..., min_length=1)
    timestamp: float = Field(default_factory=time.time)


class SessionMemory:
    """1. 会话记忆:严格保序的 FIFO 队列,常驻 Redis 内存"""
    def __init__(self, max_len: int = 4):
        self.max_len = max_len
        self.queue: List[ChatMessage] = []

    def append(self, message: ChatMessage):
        self.queue.append(message)
        # 严格控制大小,超出部分将被移出,交给短期记忆去压缩
        if len(self.queue) > self.max_len:
            evicted = self.queue.pop(0)
            logger.debug(f"[Session Memory] Evicted oldest raw message: {evicted.content[:15]}...")

    def get_raw_history(self) -> List[ChatMessage]:
        return self.queue.copy()


class ShortTermSummaryMemory:
    """2. 短期记忆:存储由 LLM 异步压缩后的滚动摘要(前情提要)"""
    def __init__(self):
        self.summary: str = ""

    def update_summary(self, expired_message: ChatMessage, current_summary: str) -> None:
        """
    生产实践中,此处会异步调用一个低成本的小模型(如 GPT-4o-mini / DeepSeek-V3)
    将老的消息融合进现有的摘要中。
    """
        logger.info("[Short-Term Memory] Triggering async LLM summary consolidation...")
        # 模拟 LLM 压缩
        mock_llm_output = f"{current_summary} [已提炼: 用户曾提及过 {expired_message.content[:20]}]"
        self.summary = mock_llm_output


class LongTermVectorMemory:
    """3. 长期记忆:连接底层的向量数据库,跨越会话持久化"""
    def __init__(self, vector_db_endpoint: str):
        self.endpoint = vector_db_endpoint
        self._mock_db: List[Dict[str, Any]] = [] # 模拟生产中的 Qdrant / Milvus / PgVector

    def semantic_search(self, query: str, user_id: str, top_k: int = 1) -> List[str]:
        """具备防御性设计的向量检索"""
        try:
            if not query.strip():
                return []
            logger.info(f"[Long-Term Memory] Executing Vector Search for User {user_id}...")

            # 模拟向量相似度匹配
            matched = [
                item["content"] for item in self._mock_db 
                if item["user_id"] == user_id and any(w in item["content"] for w in query)
            ]
            return matched[:top_k]
        except Exception as e:
            # 防御性设计:向量数据库属于外部依赖,若挂掉绝不能卡死 Agent 核心响应链路
            logger.error(f"[Long-Term Memory] Vector DB query failed: {str(e)}", exc_info=True)
            return []

    def persist_async(self, user_id: str, content: str):
        """模拟将高价值数据异步写入消息队列,最终落盘向量库"""
        try:
            self._mock_db.append({"user_id": user_id, "content": content, "ts": time.time()})
            logger.info(f"[Long-Term Memory] Async persisted high-value info to Vector DB.")
        except Exception as e:
            logger.error(f"[Long-Term Memory] Failed to persist data: {str(e)}")


class ProductionMemoryManager:
    """统一记忆管理门面 (Facade)"""
    def __init__(self, session_id: str, user_id: str, db_url: str):
        self.session_id = session_id
        self.user_id = user_id

        # 初始化三层记忆
        self.session_mem = SessionMemory(max_len=4)
        self.short_term_mem = ShortTermSummaryMemory()
        self.long_term_mem = LongTermVectorMemory(vector_db_endpoint=db_url)

        def get_context_for_llm(self, user_input: str) -> Dict[str, Any]:
        """【同步阶段】为 LLM 组装当前请求所需的全部上下文"""
        if not user_input.strip():
        raise ValueError("User input cannot be empty.")

    # 1. 并行/同步检索长期记忆
        ltm_context = self.long_term_mem.semantic_search(query=user_input, user_id=self.user_id)

    # 2. 获取短期记忆摘要与会话记忆
        return {
        "long_term_context": ltm_context,
        "short_term_summary": self.short_term_mem.summary,
        "session_history": self.session_mem.get_raw_history()
        }

        def update_pipeline_async(self, user_input: str, agent_response: str):
        """【异步阶段】对话完成后,异步更新三层记忆,解耦主线程"""
        logger.info("[Pipeline] Starting asynchronous memory updates...")

        user_msg = ChatMessage(role="user", content=user_input)
        agent_msg = ChatMessage(role="assistant", content=agent_response)

    # 1. 写入会话记忆。如果触发挤出,顺便更新短期摘要
    # 生产环境中,此处的溢出判断可以做成 Hook 挂载到消息队列
        if len(self.session_mem.queue) >= self.session_mem.max_len:
        oldest = self.session_mem.queue[0]
        self.short_term_mem.update_summary(oldest, self.short_term_mem.summary)

        self.session_mem.append(user_msg)
        self.session_mem.append(agent_msg)

    # 2. 评估是否需要提取长期记忆(关键词触发或 LLM 意图判断)
        if "修好了" in agent_response or "Bug" in user_input:
        self.long_term_mem.persist_async(
        user_id=self.user_id, 
        content=f"历史记录: 用户反馈过关于 Redis 的 Bug。最终状态: {agent_response}"
        )


# ==========================================
# 生产级验证流
# ==========================================
        if __name__ == "__main__":
        manager = ProductionMemoryManager("sess_001", "usr_100", "milvus://localhost:19530")

    # 预埋一个几个周前的长期记忆
        manager.long_term_mem._mock_db.append({"user_id": "usr_100", "content": "上周历史记录: Redis 发生 1024 内存溢出报错", "ts": 0.0})

        print("\n--- 第一轮对话:同步检索阶段 ---")
        context = manager.get_context_for_llm("我上周提的那个 Redis 报错修好了吗?")
        print(f"【LLM 看到的长期背景】: {context['long_term_context']}")
        print(f"【LLM 看到的短期摘要】: {context['short_term_summary']}")
        print(f"【LLM 看到的当前会话】: {context['session_history']}")

    # 模拟 LLM 给出了解答,触发异步更新
        manager.update_pipeline_async(
        user_input="我上周提的那个 Redis 报错修好了吗?", 
        agent_response="修好了,我们在代码里加上了边界校验,已经合并入主分支。"
        )
相关推荐
前沿科技说i3 小时前
告别赛道内卷,锚定中盘核心:国泰 A500ETF(159338)领跑均衡配置新时代
人工智能
LaughingZhu3 小时前
Product Hunt 每日热榜 | 2026-05-23
人工智能·经验分享·深度学习·神经网络·产品运营
我爱cope3 小时前
【Agent智能体9 | 反思设计模式-提示词工程的进阶法则】
人工智能·设计模式·语言模型·职场和发展
小快说网安3 小时前
当GPT Image 2遇见企业级AI大模型聚合平台:快快云云安全的接入逻辑与价值重构
人工智能·gpt·ai·chatgpt·aigc
稳如磐石.3 小时前
北京研华上架式工控机
大数据·人工智能·python
LaughingZhu3 小时前
Product Hunt 每日热榜 | 2026-05-27
前端·人工智能·经验分享·html
密瓜智能3 小时前
MIG、Time-slicing 还是HAMi?密瓜智能CEO张潇本周六亮相JuiceFS Meetup,聊聊GPU共享的生产取舍
人工智能·云原生·kubernetes·开源·gpu算力·ai算力
伊宇韵3 小时前
数字病理 WSI 图像处理流程:从大图读取到热力图可视化
人工智能
yuhulkjv3353 小时前
腾讯元宝公式粘贴word乱码
人工智能·chatgpt·word·deepseek·ai导出鸭