Agent 记忆系统设计实战:让 AI 拥有长期记忆的工程方案

Agent 记忆系统设计实战:让 AI 拥有长期记忆的工程方案

引言

大模型的上下文窗口再大,也装不下一个用户三个月的对话历史。更本质的问题是:LLM 本身是无状态的------每一次调用都是"失忆"的重新开始。如果你做过 Agent 产品,一定遇到过这些场景:

  • 用户上周说过"我用的是 Go 技术栈",这周 Agent 又推荐了 Python 方案
  • 用户明确说过"别再给我发邮件提醒",Agent 三天后又忘了
  • 跨会话任务进行到一半,新会话里 Agent 对之前的进度一无所知

这些问题的根源都一样:缺少一个设计良好的记忆系统。上下文窗口是"工作台",不是"档案室"。本文从工程视角出发,讲清楚 Agent 记忆系统的分层设计、核心机制和落地代码。

一、先分清四种记忆

心理学把人类记忆分成几类,这个模型可以直接映射到工程架构上:

记忆类型 人类对应 工程实现 生命周期
工作记忆 正在思考的内容 当前对话的 messages 上下文 单次会话
情景记忆 经历过的事件 对话历史、任务执行记录 长期
语义记忆 学到的知识 用户画像、事实三元组、知识库 长期
程序记忆 形成的技能 沉淀后的 prompt 模板、工具使用偏好 长期

很多团队只做了第一步------把对话历史塞进向量库,然后发现效果很差。原因在于:原始对话是情景记忆,用户真正需要检索的往往是语义记忆。用户说"我下个月要搬家",三个月后 Agent 应该记住的是"用户于 2026 年 9 月搬家"这个事实,而不是那句原话。

所以记忆系统的核心流水线是:原始对话 → 抽取 → 结构化存储 → 检索 → 回注上下文

二、写入策略:不是什么都要记

记忆系统最容易犯的错误是"全都存"。存储便宜,但检索成本和噪声会指数级上升。实践中我们用三层过滤:

python 复制代码
from enum import Enum

class MemoryType(Enum):
    FACT = "fact"           # 事实:用户偏好、属性、约束
    EPISODE = "episode"     # 事件:任务进度、重要决策
    PATTERN = "pattern"     # 模式:反复出现的行为规律

async def extract_memories(self, turn: DialogTurn) -> list[Memory]:
    """用 LLM 从对话轮次中抽取候选记忆"""
    prompt = f"""从以下对话中提取值得长期记住的信息。
只提取满足以下条件之一的内容:
1. 用户的稳定属性或偏好(技术栈、沟通习惯、工作时段)
2. 用户表达的明确约束("不要..."、"必须...")
3. 重要的任务进展或决策
4. 对话中没有但可以合理推断的长期事实

对话内容:
{turn.text}

输出 JSON 数组,每条包含 type/content/confidence 字段。
如果没有值得记忆的内容,返回空数组。"""
    ...

关键设计点:

  • 置信度阈值:confidence 低于 0.7 的直接丢弃,宁可漏记不可错记
  • 去重合并:新记忆入库前先检索相似记忆,相似度 > 0.9 时走更新而非新增
  • 冲突处理:用户三个月前说"喜欢简洁回复",昨天说"解释详细一点"------以时间戳新的为准,并保留版本链

三、检索策略:混合检索是标配

纯向量检索在记忆场景下有个致命弱点:时间感知缺失。"用户的手机号"这类记忆必须取最新版本,而向量检索只关心语义相似度。生产环境的标配是三路混合:

python 复制代码
def retrieve(self, query: str, user_id: str, top_k: int = 5) -> list[Memory]:
    # 1. 向量召回:语义相关性
    vec_hits = self.vector_store.search(
        query_embedding=embed(query),
        filter={"user_id": user_id},
        top_k=20,
    )
    # 2. 关键词召回:精确匹配(人名、项目名、日期)
    kw_hits = self.keyword_index.search(query, filter={"user_id": user_id}, top_k=20)
    # 3. 结构化查询:显式规则(如"用户偏好"直接查 profile 表)
    profile = self.profile_table.get(user_id)

    # 融合排序:语义相似度 × 时间衰减 × 重要性
    now = time.time()
    scored = []
    for mem in merge_dedupe(vec_hits, kw_hits):
        recency = math.exp(-mem.updated_at_days_ago / 30)  # 30天半衰期
        score = 0.6 * mem.similarity + 0.25 * recency + 0.15 * mem.importance
        scored.append((score, mem))
    return [m for _, m in sorted(scored, reverse=True)[:top_k]]

这里的时间衰减公式值得展开说:exp(-days/30) 意味着 30 天没被访问的记忆权重降到 37%。但它只能作为排序因子,不能直接删除------用户一年前提到的过敏史,语义上仍然高度相关,向量相似度会把它拉回来。

四、最小可用实现:150 行代码的 Memory Manager

下面的实现用 SQLite + 本地 Embedding,零外部依赖,可以直接跑在 Agent 项目里:

python 复制代码
import sqlite3, json, math, time

class MemoryManager:
    def __init__(self, db_path: str = "agent_memory.db"):
        self.db = sqlite3.connect(db_path)
        self.db.execute("""CREATE TABLE IF NOT EXISTS memories (
            id INTEGER PRIMARY KEY,
            user_id TEXT NOT NULL,
            type TEXT NOT NULL,          -- fact / episode / pattern
            content TEXT NOT NULL,
            importance REAL DEFAULT 0.5,
            confidence REAL DEFAULT 1.0,
            embedding TEXT,              -- JSON 数组
            version INTEGER DEFAULT 1,
            superseded_by INTEGER,       -- 被哪条记忆取代
            created_at REAL, updated_at REAL,
            access_count INTEGER DEFAULT 0
        )""")
        self.db.execute("CREATE INDEX IF NOT EXISTS idx_user ON memories(user_id, type)")

    def remember(self, user_id: str, mem_type: str, content: str,
                 importance: float = 0.5, confidence: float = 1.0):
        emb = json.dumps(embed(content))
        now = time.time()
        # 冲突检测:找语义高度相似的旧记忆
        dup = self._find_similar(user_id, emb, threshold=0.92)
        if dup:
            self.db.execute(
                "UPDATE memories SET superseded_by=NULL, version=version+1, "
                "content=?, updated_at=?, embedding=? WHERE id=?",
                (content, now, emb, dup["id"]))
        else:
            self.db.execute(
                "INSERT INTO memories (user_id, type, content, importance, "
                "confidence, embedding, created_at, updated_at) "
                "VALUES (?,?,?,?,?,?,?,?)",
                (user_id, mem_type, content, importance, confidence, emb, now, now))
        self.db.commit()

    def recall(self, user_id: str, query: str, top_k: int = 5) -> list[str]:
        """组装回注上下文的记忆块""
        rows = self.db.execute(
            "SELECT * FROM memories WHERE user_id=? AND superseded_by IS NULL",
            (user_id,)).fetchall()
        now = time.time()
        q_emb = embed(query)
        scored = []
        for r in rows:
            sim = cosine(q_emb, json.loads(r[6]))
            days = (now - r[9]) / 86400
            score = 0.6 * sim + 0.25 * math.exp(-days / 30) + 0.15 * r[4]
            scored.append((score, r))
        hits = sorted(scored, reverse=True)[:top_k]
        # 更新访问计数(供后续重要性评估)
        for _, r in hits:
            self.db.execute("UPDATE memories SET access_count=access_count+1 WHERE id=?", (r[0],))
        self.db.commit()
        return [f"- [{r[2]}] {r[3]}" for _, r in hits if _[0] > 0.35]

使用方式非常直接:

python 复制代码
memory = MemoryManager()

# 每轮对话后异步抽取写入
for mem in await extract_memories(turn):
    memory.remember(user_id, mem.type, mem.content, mem.importance)

# 每轮对话前检索回注
memories = memory.recall(user_id, user_message)
system_prompt += f"\n\n## 用户记忆\n" + "\n".join(memories)

五、生产环境的四个坑

1. 记忆污染。 用户开玩笑说"我其实是一只猫",Agent 认真记下来了,之后每次对话都围绕猫展开。防线:写入前的 LLM 抽取要加"忽略戏谑、比喻、假设性表述"的指令,且高重要性记忆需要多次出现才固化(类似人类短期记忆→长期记忆的巩固过程)。

2. 记忆膨胀与检索退化。 记忆条数超过几千条后,"先全量取再重排"的方式会拖垮延迟。方案:按 user_id 分区 + 预算控制(每次回注上下文的记忆 token 不超过 500),重要性定期重算------长期不被访问且重要性低的记忆降级归档。

3. 多用户隔离。 记忆系统一旦串了用户数据就是重大事故。除了查询层的 user_id 过滤,建议在存储层就按 user_id 做分区键,并在集成测试中加入"隔离性测试用例"。

4. 可解释性。 用户会问"你为什么知道这个?"------每条记忆必须保留来源(source_turn_id),支持"查看/修改/删除我的记忆",这既是产品信任问题,也正在成为合规要求(数据可删除权)。

六、要不要用现成方案?

如果不想从零造轮子,目前有几类选择:

  • Mem0 / Zep:开箱即用的记忆层,提供 REST API,适合快速验证
  • Letta(原 MemGPT):把记忆管理做成 OS 式分页,适合长任务 Agent
  • LangGraph / LlamaIndex 的 Memory 模块:与编排框架深度绑定,适合已用该栈的团队

但我的建议是:先用 150 行代码的极简版跑通业务闭环,理解自己业务的记忆读写模式后,再决定是否引入重型方案。记忆系统的难点从来不在存储,而在于"什么该记、什么时候想起来、记错了怎么办"------这些是业务问题,不是框架问题。

总结

Agent 记忆系统的工程要点浓缩成四句话:

  1. 分层建模:工作记忆靠上下文,长期记忆靠"抽取→结构化→检索"流水线
  2. 写入有门槛:置信度过滤 + 去重合并 + 冲突以新替旧
  3. 检索要混合:向量 + 关键词 + 时间衰减 + 重要性加权
  4. 治理不能少:防污染、控膨胀、强隔离、可解释

记忆是 Agent 从"工具"变成"伙伴"的分水岭。当你的 Agent 能在第三十次对话时自然地说出"上次你提到的那个 Go 项目进展如何了?"------用户留下的那一刻,就是记忆系统的价值证明。

相关推荐
天天被压力1 小时前
【零依赖量化数据实战 #31】沪深A实时盘口全景:逐笔·全盘实时·最新价·历史逐笔
java·人工智能·python
Mr数据杨1 小时前
小样本图像分类实战 Cleaned vs Dirty V2 盘子清洁识别案例解析
人工智能·数据分析·kaggle竞赛
zzzll11111 小时前
Ollama 本地大模型部署与使用指南
人工智能
天辛大师1 小时前
天辛大师浅谈AI时代,“学会学习“本身的内涵也需要被重新定义?
人工智能·深度学习·学习·启发式算法·量子计算
学弟1 小时前
【系列梳理】文档解析系列梳理
人工智能·ocr
幂律智能1 小时前
穿透到合同,管控到付款
大数据·人工智能
咸鱼老弟1 小时前
用 Cursor Rules / CLAUDE.md 把团队规范"固化"进 AI 编程工作流
ai编程
今天AI了吗1 小时前
【AI】一文讲清 RAG:从大模型局限到企业级知识库落地流程
前端·javascript·人工智能·python·深度学习·机器学习·easyui
“AI国潮设计-小江”1 小时前
《Python实战 | 用SDXL大模型生成“潮汕英歌舞”国潮IP头像,已申请外观专利,附Prompt思路!》
人工智能·python·prompt·aigc·scikit-learn