拆解 DeerFlow Memory:Agent 的长期记忆到底怎么做?

前面我们已经看了 DeerFlow 的可观测性、Clarification 这些设计。

这一篇继续看一个 Agent Harness 里很重要的东西:Memory。

现在很多 Agent 都会说自己有"长期记忆",但是记忆这个东西是怎么来的?

text 复制代码
用户说了一句话
    ↓
LLM 提取重点
    ↓
存到向量数据库
    ↓
下次聊天再搜索出来

我一开始也以为 DeerFlow 差不多是这么做的。让我们开始看看具体是怎么实现的吧!真实的设计更复杂也更简单!

比如:

  • 存储记忆的过程的设计;
  • 不是 LLM 说要记就直接记;
  • 默认甚至没有用向量数据库。

1. DeerFlow 到底把什么叫做 Memory

先区分三个东西。

类型 作用 生命周期
会话状态 当前线程里的消息、运行状态 当前线程
上下文摘要 对旧消息做压缩,避免上下文越来越长 当前线程
长期记忆 保存以后聊天还可能有用的信息 跨会话

DeerFlow 这里说的 Memory,主要是第三种。

比如:

text 复制代码
用户主要使用 Python 和 LangGraph
用户喜欢简洁一点的技术回答
用户长期在做 Agent 相关项目

这些东西下一次新开一个会话,可能依然有用。

但下面这些一般就不应该进长期记忆:

text 复制代码
这个 PR 先用 Redis
今天帮我删除这个文件
当前这个 bug 暂时这么绕过去

所以长期记忆真正麻烦的地方其实不是"存"。

而是:

什么东西值得存?

2. after_agent() - agent跑完之后,并不是直接写 Memory

默认情况下,DeerFlow 使用的是 middleware 模式。

每一轮 Agent 跑完之后,会进入:

python 复制代码
MemoryMiddleware.after_agent()

然后调用:

python 复制代码
get_memory_manager().add(
    thread_id,
    messages,
    agent_name=agent_name,
    user_id=user_id,
    trace_id=trace_id,
)

add() 不是直接新增memory,如果这样memory直接爆炸了

其实是:

把这批对话提交给 Memory 系统,让它判断有没有东西值得记。

真正的链路大概是:

text 复制代码
Agent 一轮执行结束
        ↓
MemoryMiddleware
        ↓
DeerMem.add()
        ↓
消息过滤
        ↓
debounce 队列
        ↓
MemoryUpdater
        ↓
Memory LLM
        ↓
代码校验
        ↓
真正写入 Memory

3. 写入之前,先把大部分垃圾过滤掉

DeerMem 收到消息以后,并不会马上扔给模型。

它先做一轮比较确定性的处理。

比如:

  • 工具调用不要;
  • 工具返回不要;
  • 框架隐藏消息不要;
  • 上传文件之类的临时信息不要;
  • "好的""谢谢""OK"这种确认信息也没必要;
  • 没有有效用户消息或者 AI 回复,直接跳过。

除此之外,它还会检测一些信号,比如:

text 复制代码
correction
preference
identity
goal
decision

比如用户说:

text 复制代码
不是,我以后主要写 Python,不写 Java 了。

这里同时有:

text 复制代码
correction
+
preference

这种内容显然比:

text 复制代码
好的

更值得让 Memory LLM 看一眼。

不过这里有个细节。

这些规则并不会直接决定保存。

它们只是先筛掉明显没价值的东西。

最后到底记不记,还是要进入下一层。


另外 DeerFlow 这里还做了一个挺实用的优化:debounce。

默认是:

yaml 复制代码
debounce_seconds: 30

意思不是每 30 秒处理一次,而是等待用户暂时停止输入。

这样连续聊几轮时,不需要每轮都单独跑一次 Memory LLM。

这里还有一个 watermark。

middleware 有时候传过来的是完整会话,但 Memory 系统不会每次把整段聊天重新抽一遍。

本质上就是:

text 复制代码
debounce
解决重复调用

watermark
解决重复处理

4. 最关键的一层:LLM 提议,代码裁决

过滤之后,真正需要处理的消息才会进入 MemoryUpdater。

这里会调用一个专门的 Memory LLM。

Prompt 里面大概会放:

  • 当前已经存在的 memory;
  • watermark 后的新消息;
  • correction / preference 等信号;
  • 一些需要重新检查或者合并的旧 fact。

然后让模型返回结构化结果。

类似:

json 复制代码
{
  "user": {
    "workContext": {
      "summary": "用户主要使用 Python 和 LangGraph",
      "shouldUpdate": true,
      "scope": "user",
      "authority": "descriptive"
    }
  },
  "newFacts": [
    {
      "content": "用户偏好简洁的技术回答",
      "category": "preference",
      "confidence": 0.9,
      "scope": "user",
      "durability": "durable",
      "authority": "descriptive"
    }
  ]
}

这里我觉得是 DeerFlow Memory 里最值得看的一个设计:

LLM 只负责提议,代码才负责最终决定。

比如一个 fact 想真正写进去,默认需要满足:

text 复制代码
scope = user
durability = durable
authority = descriptive
confidence >= 0.7

代码要继续判断:

text 复制代码
是不是用户级信息?
是不是长期有效?
是不是描述性事实?
置信度够不够?
是不是重复了?
fact 数量有没有超限?

通过之后才写。

5. 一个挺反直觉的地方:DeerMem 默认没用向量数据库

说到长期记忆,很多人的第一反应应该都是:

text 复制代码
Embedding
+
Vector DB

但 DeerMem 默认不是这么做的。

它真正保存 Memory 的地方其实非常朴素:

text 复制代码
{storage_path}/
├── users/{user_id}/memory.json
└── users/{user_id}/agents/{agent_name}/facts/.../*.md

简单理解:

text 复制代码
memory.json

放一些用户级 summary。

text 复制代码
facts/*.md

保存具体 fact。

而搜索默认走的是:

text 复制代码
SQLite FTS5
+
BM25

也就是:

text 复制代码
facts/*.md
    ↓
建立 FTS5 索引
    ↓
BM25 排序

不是 Chroma,不是 Milvus,也不是 FAISS。

因为长期记忆的数据量通常并没有大家想象得那么大。

一个用户就算存几十条、上百条 fact:

text 复制代码
100 条 memory

这个规模用 FTS5 完全够用。

反而如果一开始就引入:

text 复制代码
Embedding
Vector DB
向量同步
索引更新
模型版本

系统复杂度一下子就上来了。

所以 DeerFlow 这里把:Markdown / JSON作为权威数据,FTS5 只是派生索引。即使索引删了,也可以重新构建。

这是一个很典型的 Harness 思路:

能简单解决的问题,不急着上重型基础设施。

6. 写进去之后,下次 Agent 怎么看到?

最后还有一个问题:

Memory 已经保存了,下次聊天模型怎么知道?

DeerFlow 会在动态上下文 middleware 里读取当前用户的长期记忆,然后格式化成类似:

xml 复制代码
<memory>
User Context: ...
History: ...
Facts: ...
</memory>

再放回 Agent 的上下文里。

所以完整链路其实是:

text 复制代码
上一轮聊天
    ↓
MemoryMiddleware
    ↓
过滤 / debounce / watermark
    ↓
Memory LLM
    ↓
代码门禁
    ↓
JSON / Markdown
    ↓
FTS5

到了下一次聊天:

text 复制代码
读取 Memory
    ↓
格式化
    ↓
注入 Agent Context
    ↓
LLM

这样模型才会表现出:

text 复制代码
"它还记得我之前说过什么。"

DeerFlow 这里还有个设计。

Memory 内容不会直接当成最高权限的 System Prompt 塞进去。它通常作为隐藏上下文注入

原因也很好理解:

Memory 本质上来自用户,不能因为被保存了一次,就突然获得 System 权限。


DeerFlow 还提供了另一种 tool 模式。

也就是不给 Agent 自动处理,而是提供:

text 复制代码
memory_search
memory_add
memory_update
memory_delete

让模型自己决定什么时候读、什么时候写。

但从设计上看,它们底层并不是两套 Memory。

只是两种入口:

text 复制代码
middleware 模式
→ 系统自动管理

tool 模式
→ 模型主动管理

最终还是走同一个 MemoryManager backend。

最后

把 DeerFlow 的 Memory 拆完之后,感觉DeerFlow 给出的方案其实没有特别玄学:

text 复制代码
规则先过滤
    ↓
LLM 做语义判断
    ↓
代码做最终门禁
    ↓
简单可靠地持久化

甚至连检索都没有急着上向量数据库。

从这个角度看,Memory 其实和前面讲的 Clarification、可观测性很像。

真正重要的是把模型前后的这些边界控制好。

相关推荐
Hi202402171 小时前
Vortex CUDA 生态适配:让 CUDA C、CUTLASS 与 Triton 在 RISC-V GPGPU 上运行
人工智能·risc-v·gpgpu
龙腾AI白云4 小时前
AI检索增强生成(RAG):解决大模型幻觉的核心落地技术
数据库·人工智能·机器学习·知识图谱
云票4 小时前
企业对接AI合同审查系统的工程实践
人工智能
admin and root4 小时前
「AI安全篇」实战AntiDebug自动化JS逆向加解密MCP
javascript·人工智能·网络安全·自动化·漏洞挖掘·cnvd·src赏金
智能RPA4 小时前
智能体自动化平台与主数据管理平台(MDM)对比评测
人工智能·自动化·agent·rpa
跨境小彭5 小时前
Temu拉美站点铺货实操复盘:手动复制痛点与批量自动化解决方案
服务器·人工智能·搜索引擎·自动化·temu电商运营
封印师请假去地球钓鱼5 小时前
边解边变的问题:从“决策依赖“一词出发
人工智能·算法
AI搅拌机6 小时前
ComfyUI管理大师:安全稳定升级+切换指定版本!
人工智能
浅安的邂逅6 小时前
20929-OpenAI 一天踩三脚急刹:暂停前沿训练、叫停 Astra、披露越权访问澳政府网站
人工智能·大模型·ai编程·行业动态·ai日报
Qyr996 小时前
2026-2032直接芯片液冷板市场爆发式增长:AI算力浪潮下的热管理核心赛道
大数据·人工智能