笔记 | 给 Agent 装一个真正能用的记忆层:2026 年工程实战

最近在做优化的时候涉及到了这块内容,觉得值得写下来,方便以后翻阅。

给 Agent 装一个真正能用的记忆层:2026 年工程实战

摘要:让 Agent 在跨会话中记住用户的技术栈和偏好,是走向生产级应用的关键。不少开发者以为记忆只是“存聊天记录再塞进 Prompt”,但在生产环境,这根本行不通。一个真正可用的记忆层,务必解决事实提取、矛盾消解、时序感知和多租户隔离等核心挑战。这篇这篇从工程实战出发,带你完整实现一套基于 Mem0 的独立记忆层,并对比 LangGraph 内置方案与极简结构化方案,帮你选对记忆架构。
这篇标签:#Agent记忆 #Mem0 #LangGraph #LLM应用 #架构设计 预计阅读时间:8 分钟

这篇目录

方案二:LangGraph 内置记忆(工作流原生状态机)方案三:极简结构化方案(反潮流的 SQLite/JSON)三种方案的工程定位与对比工程实践中容易踩的 4 个坑

为什么你得一个真正的记忆层

假设你让编程助手重构代码。第一天聊了半小时,交代了技术栈是 FastAPI + PostgreSQL,代码偏好 black 格式化,命名用 snake_case

第二天打开新会话,它开口第一句问你:“请问你用什么框架?”

这就是绝大多数 AI 应用的现状:LLM 就像只有 7 秒记忆的金鱼。上下文窗口一关,一切归零。

扩窗口只能延缓发作,无法根治。真正的解法,是外挂一层独立的记忆系统(Memory Layer)。让它像私人秘书一样,自动积累画像与上下文,跨会话按需召回。

这篇文章,我们就从工程角度,完整实现一套基于 Mem0 的 Agent 记忆层,并给出 LangGraph 内置方案和极简结构化方案作为对比选型。

记忆层到底要解决什么麻烦

不少人以为“Agent 记忆”就是“把对话存进向量库,下次捞出来拼进 Prompt”。这太浅了。

如果把记忆层当成 Agent 的“大脑海马体”,一个生产级的记忆系统至少要处理以下五件事:

1. 事实提取:长篇大论中真正有价值的是原子事实。比如“团队用 FastAPI 做 API,PostgreSQL 存数据”——价值在于(技术栈 = FastAPI、数据库 = PostgreSQL),而非原话。死存原文只会让检索时 Token 被废话挤满。
2. 矛盾消解:用户上周说“用 PostgreSQL”,这周说“已迁移到 MongoDB”。系统务必识别这是状态更新并自动覆盖,而不是两条都存,让 Agent 检索后自己猜。
3. 时序感知:有些信息带保质期。比如“本周五要发布 v2.0”,到了下周一就该废弃。系统得能按时间衰减或过滤。
4. 多租户隔离:不同用户的记忆绝对不能串线。Alice 的技术偏好,绝不能污染 Bob 的会话上下文。
5. 检索精度:简单的余弦相似度 Top-K 远远不够。硬阈值过滤(如距离 < 0.5 才算相关)在实际工程中极度脆弱,因为不同 Embedding 模型的距离分布差异极大,固定阈值没法打天下。

明白这五点,就知道为什么“手搓 Chroma + Sentence-Transformers”的草台班子在生产中站不住——它只解决了最基础的“存取”,剩下四个硬骨头全得自己啃,且极难写稳。

方案一:Mem0 工程实战(大而全的独立基建)

Mem0 是目前 Agent 记忆领域的事实标准(GitHub 近 7 万 Star,LoCoMo SOTA)。它的核心思路是:用 LLM 驱动记忆的全生命周期管理。提取、去重、更新、消解矛盾,全交由模型在底层自动做好。开发者只需调 addsearch

环境准备

pip install mem0ai openai python-dotenv

# .env
OPENAI_API_KEY=sk-你的key

默认情况下,Mem0 会用 gpt-4o-mini(或其他廉价小模型)做事实提取,用 text-embedding-3-small 做向量化。单次记忆提取的 Token 消耗极低,不用担心成本爆炸。

初始化与基础操作

import os
from dotenv import load_dotenv
from mem0 import Memory

load_dotenv()

# 一行初始化,数据默认持久化到本地
m = Memory()

# 添加记忆 —— Mem0 会自动用 LLM 提取原子事实,而非原样存储
m.add(
    "我们团队用 FastAPI 做 API 层,PostgreSQL 存数据,Redis 做缓存",
    user_id="dev_alice"
)
# 此时底层实际存储的是:
# "技术栈包含 FastAPI" / "数据库使用 PostgreSQL" / "使用 Redis 缓存"

# 检索记忆
results = m.search("项目用的什么技术栈", user_id="dev_alice")
for r in results:
    print(f"  {r['memory']}")

# 获取某用户的全部记忆
all_mem = m.get_all(user_id="dev_alice")

# 更新记忆(当用户状态发生改变时)
m.update(memory_id="xxx", data="数据库已从 PostgreSQL 迁移到 MongoDB")

# 删除单条废弃记忆
m.delete(memory_id="xxx")

如果你希望保留对话原文(退化为传统向量库),可以把 add 方法的 infer 参数设为 False

完整 Agent 集成示例

写个“技术支持 Agent”排查项目麻烦。接入 Mem0 后,它能在对话中自动调取历史技术栈与环境信息,再也不用反复盘问。

import os
from dotenv import load_dotenv
from mem0 import Memory
from openai import OpenAI

load_dotenv()

memory = Memory()
llm = OpenAI()

def chat(user_query: str, user_id: str = "dev_alice") -> str:
    # 1. 召回相关记忆
    memories = memory.search(query=user_query, user_id=user_id)

    # 2. 将记忆格式化为上下文
    if memories:
        context = "\n".join(f"- {mem['memory']}" for mem in memories)
    else:
        context = "(无已知上下文)"

    # 3. 构建带记忆的 prompt
    prompt = f"""你是一个技术支持助手,帮开发者排查项目问题。

已知用户上下文:
{context}

用户当前问题:{user_query}

要求:
- 充分利用已知上下文,不要重复询问已有信息
- 如果上下文不足,主动询问缺失的关键信息
- 回答简洁、聚焦问题本身"""

    # 4. 调用 LLM 生成回答
    resp = llm.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.3
    )
    answer = resp.choices[0].message.content.strip()

    # 5. 存入新记忆(Mem0 会在后台自动提取事实、去重、消解矛盾)
    memory.add(
        messages=[
            {"role": "user", "content": user_query},
            {"role": "assistant", "content": answer}
        ],
        user_id=user_id
    )

    return answer

if __name__ == "__main__":
    print("技术支持助手已就绪(输入 '退出' 结束)\n")
    while True:
        q = input("你:")
        if q.strip() == "退出": break
        print(f"助手:{chat(q)}\n")

运行后模拟几轮对话,效果一目了然:

你:我们项目用 FastAPI + PostgreSQL,最近 API 响应变慢了
助手:了解,FastAPI + PostgreSQL 环境。响应变慢可能的原因有很多,
      能描述一下是所有接口都慢还是特定接口吗?大概从什么时候开始的?

你:主要是 /orders 这个接口,从上周上线新功能后开始慢
助手:/orders 接口在上周新功能上线后变慢,大概率是新功能引入的查询
      问题。建议先检查:1)新功能是否加了新的 DB 查询;
      2)用 EXPLAIN ANALYZE 看 /orders 的 SQL 执行计划...

你:查了一下,是少了个索引,加上了现在快多了
助手:好的,问题定位是 /orders 查询缺少索引。建议后续在 CI 里加上
      慢查询检测,避免类似问题再次上线。

关键在第三轮——Agent 记住了第一轮的技术栈(FastAPI + PostgreSQL)和第二轮的问题接口(/orders)。底层存的不是流水账,而是提纯后的事实:“技术栈 FastAPI + PostgreSQL”、“根因是缺少索引”。

定制提取逻辑与换用国产大模型

Mem0 相当灵活,你可以自定义 LLM 应该提取哪些核心信息,剔除闲聊废话:

from mem0.configs.base import MemoryConfig

custom_prompt = """从以下对话中,只提取这两类信息:
1. 技术环境:框架、数据库、部署环境、版本号
2. 问题诊断:错误现象、根因、解决方案
忽略所有闲聊和其他无关内容。

对话内容:{content}
输出 JSON:{"facts": ["..."]}
"""

config = MemoryConfig(custom_prompt=custom_prompt)
m = Memory(config=config)

同样,你可以把底层的 LLM 和 Embedding 模型全部换成通义千问、智谱等兼容 OpenAI 接口的国产方案,或者将存储后端切换为团队现有的 Qdrant、Chroma 向量库。

方案二:LangGraph 内置记忆(工作流原生状态机)

如果项目已在用 LangGraph,好消息是:记忆已作为基础设施内置,无需外挂 Mem0。

LangGraph 的记忆分为两层:

  • 短期记忆:靠 Checkpoint 机制自动给每次对话状态打快照,通过 thread_id 隔离不同会话。
  • 长期记忆:靠 BaseStore 跨会话存储事实,支持向量检索和精确的关键词检索。
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.store.memory import InMemoryStore

# 短期:自动状态快照,thread_id 隔离会话
checkpointer = InMemorySaver()

# 长期:跨会话事实,namespace 隔离用户
store = InMemoryStore()

app = graph.compile(checkpointer=checkpointer, store=store)

# 写入长期记忆事实
store.put(
    namespace=("user_profile", "alice"),
    key="tech_stack",
    value={"data": "FastAPI + PostgreSQL + Redis"}
)

# 基于语义搜索长期记忆
memories = store.search(
    namespace=("user_profile", "alice"),
    query="技术栈"
)

取舍很明确:LangGraph 优势是零额外依赖,与工作流无缝融合。代价是它很“老实”——存什么是什么,不会主动做“事实提取”和“矛盾消解”。用户改口了,更新逻辑得自己写。

方案三:极简结构化方案(反潮流的 SQLite/JSON)

大家都卷向量库时,有个“反潮流”信号:谷歌开源的 Always On Memory Agent 不用向量库,不跑 Embedding,直接用 SQLite 存结构化记忆,全靠精确匹配。

这背后的逻辑很清醒:绝大多数工程场景,根本不需要语义模糊匹配。

用户偏好、环境配置等都是明确的 KV 键值对。强上向量库,反而容易因相似度飘忽而翻车。直接用 JSON 或 SQLite 存,更快、更准、更省。OpenClaw 的 MEMORY.md 也是这思路。

import json
from datetime import datetime
from pathlib import Path

class StructuredMemory:
    """极简结构化记忆,专治花里胡哨,适合事实明确的业务"""

    def __init__(self, path="memory.json"):
        self.path = Path(path)
        self.data = self._load()

    def _load(self):
        if self.path.exists():
            return json.loads(self.path.read_text())
        return {"facts": {}}

    def set(self, key: str, value: str):
        """写入/更新一条事实(同名 Key 直接覆盖,天然防冲突)"""
        self.data["facts"][key] = {
            "value": value,
            "updated_at": datetime.now().isoformat()
        }
        self._save()

    def get(self, key: str):
        return self.data["facts"].get(key, {}).get("value")

    def _save(self):
        self.path.write_text(json.dumps(self.data, ensure_ascii=False, indent=2))

# 用法:直接覆盖,告别矛盾消解的烦恼
mem = StructuredMemory()
mem.set("tech_stack", "FastAPI + PostgreSQL")
mem.set("db_migrated_to", "MongoDB")  # 用户改口,直接替换旧值

这个方案天然免疫矛盾冲突(同名 Key 直接覆盖),天然支持多用户隔离(每人建个独立文件),且检索速度极快。唯一的缺点是:当用户用自然语言问“用的什么框架”时,你需要自己写一套规则把“框架”映射到 tech_stack 这个 Key 上。

三种方案的工程定位与对比

维度Mem0LangGraph 内置极简结构化 (JSON/SQLite)核心定位大而全的独立记忆基础设施工作流框架原生集成的状态机轻量级的配置/属性读写系统事实提取✅ LLM 自动提取压缩❌ 无(存什么是什么)❌ 无矛盾消解✅ 内置自动覆盖逻辑❌ 无(需手写业务逻辑)✅ 手动覆盖(KV 天然防冲突)语义检索✅ 强(模糊匹配体验极佳)✅ 支持❌ 不支持(需精确命中 Key)开销与依赖❌ 引入新库,且有模型提取开销✅ 零额外依赖,零提取成本✅ 极致轻量,响应最快适用场景自然语言交互多、提问多变业务已深度绑定 LangGraph 生态记忆事实明确、规模小、属性导向

选型建议口诀

1. 要模糊匹配,上 Mem0。提问方式多变时,Mem0 把提取和冲突处理全自动化,投入产出比最高。
2. 已在 LangGraph 生态,用内置。短期 Checkpoint,长期 Store,别强行引入外部依赖徒增繁琐度。
3. 事实明确且规模小,别迷信向量库。直接 KV 存储,精确查找永远比语义检索靠谱。

工程实践中容易踩的 4 个坑

1. 盯紧记忆提取的成本:Mem0 每次 add 都会调一次 LLM。高频对话下,这笔费用会积少成多。你可以通过 infer=False 过滤掉低价值的寒暄,或者专门切一个小模型(如 gpt-4o-mini)来干提取的脏活。
2. 警惕记忆库“虚胖”拖慢检索:虽然 Mem0 有去重,但时间久了依然会积累大量历史条目。建议定期跑脚本审查,或者利用 Metadata 里的时间戳做 TTL(生存时间)过期清理。
3. 多 Agent 协作时小心“记忆串味”:如果多个 Agent 服务同一个用户,务必利用 agent_id 字段区分各自的知识域。千万别让“写代码的 Agent”读到了“点外卖 Agent”的上下文。
4. 中文场景慎选 Embedding 模型:如果你发现语义检索老是命中一些奇怪的东西,考虑把默认的 text-embedding-3-small 换成国产专门针对中文优化的向量模型(如通义 text-embedding-v2)。

Agent 记忆从“手搓向量库”演进到“独立记忆基建”,核心不是换技术栈,而是思路跃迁——记忆不再是存储问题,而是知识管理问题。让 LLM 亲自下场做事实提取和冲突消解,把开发者从手写过滤逻辑中解放出来,这才是当前 Agent 记忆层的正确打开方式。


今天的内容大概就这些,实际开发中大家还会遇到更多细节,欢迎留言分享自己的经验。

评论 (0)

暂无评论