拆解 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、可观测性很像。

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

相关推荐
格尔曼Noah2 小时前
文本转音频技术选型指南:2026年主流TTS与端到端语音大模型深度对比
人工智能·语音识别
hans汉斯2 小时前
采煤工作面隐患目标检测中YOLO11与Faster R-CNN的对比研究
人工智能·目标检测·计算机视觉·cnn·信息与通信·信号处理
神奇小汤圆2 小时前
从 0 用 AgentTeams 搭一个多 Agent 模拟面试系统
人工智能
潘正翔2 小时前
DeepSeek Harness从0到1部署
人工智能·开发·codex·deepseek·harness·deepseekharness·cludecode
bulingg2 小时前
bert输入长度有限,如何处理超长文本?
人工智能·深度学习·bert
神奇小汤圆2 小时前
DeepSeek Harness 这波,搞得全世界都在安装 Node.js
人工智能
nanawinona2 小时前
2026年手工思路量化后,工具重点会怎样变化
人工智能·python
武子康2 小时前
DeepSeek Harness、Codex、Claude Code、LangGraph 应该怎么选:先判断你缺的是产品、底盘还是工作流
人工智能·llm·agent
美团技术团队2 小时前
美团搜索3.0:LLM 语义表征在排序模型的探索与应用
人工智能