news 2026/10/28 23:58:15

深入拆解DeerFlow长期记忆:写入、召回与覆盖的工程实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入拆解DeerFlow长期记忆:写入、召回与覆盖的工程实现

我第一次把 DeerFlow 跑起来的时候,其实有点不以为然——不就是把大模型的对话流程做成 DAG 图吗,编排节点、串接工具、再加上流式输出,这类框架这两年见得多了。真正让我停下来多看了一会儿的,是里面“长期记忆”那一块的设计。它不只是把聊天记录扔进向量库里就算完事,而是把记忆当成了一套有写入策略、有检索机制、有衰减淘汰规则的独立子系统。

这篇文章就从我自己的理解出发,用一个最小可运行的示例,拆一下字节开源项目 DeerFlow 的长期记忆实现思路。你会发现,长期记忆这件事想要做好,真正麻烦的地方根本不在“存得下”,而在“记得准、忘得对、用得上”。这篇文章适合正在做智能体开发、想给自己 Agent 加上跨会话记忆能力的开发者,也适合对 AI 应用架构感兴趣的人。

1. 先搞清楚:智能体的长期记忆到底解决什么问题

我们先把话说明白。一个没有记忆的智能体,本质上就是一个“每次都被清零重来的对话接口”。你上轮告诉它“我是后端团队的,这边数据库统一用 PostgreSQL,所有 schema 变更都要走评审流程”,过两天你再问它“我改个表结构要注意什么”,它一脸茫然。这不是模型笨,而是状态没有被持久化。

长期记忆要解决的,就是让智能体在跨越多个会话、多个时间点之后,仍然能保持对用户背景、偏好、历史决策的连续理解。这在真实业务里太重要了。我做过的几个智能体项目里,用户最大的抱怨往往不是“模型回答不准”,而是“它老是不记得我说过什么”。尤其是 To B 场景,用户的岗位角色、技术栈、组织流程这些信息,一旦每轮都要重新交代,体验就彻底崩了。

DeerFlow 的长期记忆方案,给我的感觉是它在“记忆”这件事上想了三个层次的问题:

第一,记忆怎么写。不是所有对话都值得长期保存,必须要有筛选和提炼,否则存得越多,噪音越大,检索到的质量就越差。第二,记忆怎么读。不是每次回复都要把所有历史捞出来,要在合适的时机、合适的粒度下召回相关的片段。第三,记忆怎么更新。用户的偏好是会变的,去年的技术栈今年可能就换了,旧记忆必须能被覆盖和纠正。

这三点看着简单,真正落地的时候,每一层都有坑。DeerFlow 的思路是把它们拆成记忆节点,编排进 Agent DAG 里,而不是让模型自己瞎记。这个设计上的克制,我觉得是它最值得学习的地方。

2. 长期记忆实现的核心设计拆解

在 DeerFlow 的工作流里,长期记忆不是一个统一的大黑盒,我更愿意把它拆成三条逻辑链来看:存储层、写入链路、读取链路。

2.1 存储层:记忆数据是怎么组织的

先说存储。DeerFlow 风格的记忆存储不会把所有内容混在一个桶里。我见过很多自研 Agent 项目,把历史对话一股脑塞进向量库,然后每次查询直接 top-k,这种做法初期能用,等到数据量上来就越来越钝。原因很简单:向量检索适合找“语义相似的片段”,不适合找“结构性的事实”。

DeerFlow 给我的启发是,记忆数据要按类型分层:

  • 短期会话上下文,保存在对话工作流内部,会话结束就清理。
  • 场景记忆,记录类似“用户上次在调试支付回调”这种和当前任务相关的中间状态,保留几周或几个月。
  • 长期用户画像,记录“用户是后端开发、偏好 PostgreSQL、变更需要评审”这类稳定的事实,生命周期很长。

对长期用户画像这类数据,我用过两种方案来落存储:一种是用关系型数据库或 KV 存结构化的“属性槽”,比如userId + attribute + value + updatedAt;另一种是用向量库存语义化的记忆片段,按用户 ID 做命名空间隔离。DeerFlow 的做法更偏后者,同时会给记忆条目带上 namespace、时间戳、来源事件 ID 这类元数据。这非常关键,没有元数据的记忆库,后续做过期策略和冲突更新的时候你会很痛苦。

存储类型适合什么典型实现生命周期
内存缓存当前对话轮次的中间状态Redis / 进程内 Map分钟级
KV 存储用户属性、偏好槽位Redis / MySQL月级或年级
向量库语义化的经验片段、历史结论Chroma / Milvus / pgvector周级或年级
图存储实体之间的关系记忆Neo4j / NebulaGraph长期

2.2 写入链路:不是所有对话都值得记住

这是我要重点讲的部分。我见过最典型的翻车做法,是把用户每一轮说的话都原封不动地写入记忆库。结果是什么?召回的时候什么都是“相关”的,又什么都不可靠。用户随口一句“今天天气不错”,也会和“他在考虑迁移数据库”这种重要信息混在一起。

DeerFlow 给我的感觉是,它在记忆写入前加了一个“提炼”动作。说白了,就是在工作流里单独放一个节点,用 LLM 把当前对话中值得长期保留的信息抽出来,格式化成结构化的记忆条目。比如用户说了三句闲聊、一句关键决定,记忆写入节点就只抽出那一句关键决定,并打上意图标签和置信度。

我自己常用的做法是让提炼节点输出这样的格式:

{ "fact": "用户所在团队统一使用 PostgreSQL", "type": "preference", "importance": 0.9, "expire_at": "2026-01-01T00:00:00Z", "source": "session_1243" }

这里最重要的两个字段是type和importance。type决定这条记忆会被放到哪一类记忆池,importance决定后面做压缩淘汰时,谁先被挤出局。DeerFlow 这类框架虽然不同版本内部实现不太一样,但把“记忆写入”当成一个独立可编排的节点,这个思路是共通的。

2.3 读取链路:记忆召回要和当前问题强绑定

写入做完了,读取才是决定体验的胜负手。很多团队把读取做成“每次请求都历史全量召回”,这其实很蠢。第一浪费 token,长上下文一旦超过模型窗口,性能反而下降。第二是干扰模型判断,不相关的老黄历会带偏回答。

我会在读取链路里做三步:

第一步是意图判断。先让一个轻量节点判断当前这轮问题需不需要长期记忆。如果用户就是随口问一句“你好”,完全没必要查库。

第二步是查询改写。把当前问题改写成更适合检索的形式。比如用户问“我改个表要注意什么”,如果用户画像里有“PostgreSQL”这个事实,那改写后的查询可能就不只是“改表注意事项”,而是“PostgreSQL 改表注意事项 + 团队流程约束”。

第三步是阈值过滤和重排。向量检索出来的 top-k 并不直接可用,还要过滤掉相似度低于阈值的条目,再按重要度和时间衰减重新排序。这里有个参数值得关注:时间衰减。一年前的一条记忆,即使语义相关,优先级也要降一降,除非它是“团队统一使用 PostgreSQL”这种不会过期的硬约束。

读取链路做好了,你会明显感觉到智能体像是“记得你”的,而不是在靠系统提示词里塞一堆历史记录硬撑。

3. 一个可运行的示例:跨会话记住用户偏好

这里我写一个简化但完整的示例,带你走一遍 DeerFlow 式的长期记忆工作流落地流程。场景是做一个“后端项目小助手”,要求它能跨会话记住用户的技术栈偏好和团队约束。

3.1 工作流整体结构

我在 DeerFlow 里会把工作流设计成这样的 DAG,节点之间有明确的输入输出,不搞隐式共享变量:

input_user_message | v memory_recall_node | v agent_reason_node | v output_stream_node | v memory_write_node

有人会问,为什么把memory_write_node放在最后而不是在回复之前?因为你要写入的“事实”,往往要在模型完成推理之后才能确认价值。比如模型通过推理判断出用户那句话确实是一个新的长期偏好,再写库。当然你也可以并行去写,但我建议先用一个独立的写节点,方便做失败重试和日志追踪。

3.2 流程一:用户首次说出关键偏好

假设用户说:“我这边后端主要用 PostgreSQL,以后凡是涉及数据库迁移的改动,都得先在群里同步一下。”

DeerFlow DAG 跑起来大概是这么个过程。

memory_recall_node第一阶段检索,因为这是新用户,记忆库为空,所以没有召回任何条目,返回一个空的上下文片段。此时工作流不会报错,它会把“无召回”当成一个合法状态,只是提示信息减少。

然后agent_reason_node拿到原始用户消息,正常生成回复,可以回应说“明白了,后续涉及 PostgreSQL 迁移的事项,会先提示你走群内同步流程”。

关键动作发生在最后。memory_write_node先做提炼,把上面两段信息拆成两条记忆:

  • 事实一:用户后端技术栈是 PostgreSQL,类型为long_term_attribute,重要度 0.9。
  • 事实二:数据库迁移改动需要先在群内同步,类型为workflow_rule,重要度 1.0。

两条记忆写入向量库,同时更新结构化用户画像表里的db_tech_stack字段。注意,这里我不仅写向量库,还写结构化槽位。当模型需要精确的偏好值时,结构化槽位比向量召回稳定得多。DeerFlow 方案在我理解里也支持这种多模态的记忆写入,所以别只盯着向量库。

3.3 流程二:几天后用户追问数据库迁移问题

过了几天,用户带着几乎完全不同的语气回来问:“我刚看了一下当前库里的分区表,有点想改成分区策略,但怕影响线上,我该注意什么?”

如果是一个无记忆的 Agent,它只能根据这句话进行泛泛回答。但在 DeeerFlow 式的工作流里,memory_recall_node会先做查询改写,生成类似“PostgreSQL 分区表变更注意事项 + 是否涉及迁移 + 团队同步要求”的检索条件。然后从长期记忆库里召回两条高相关条目:

  • “用户后端技术栈是 PostgreSQL”
  • “数据库迁移改动需要先在群内同步”

这两条并不是以完整聊天记录的形式存在于记忆库,而是经过提炼的独立事实。模型在生成回答时,会被明确告知这两条来自长期记忆,并且带上置信度和时间信息。这样生成出来的回答就会变成:

“你这个分区调整本质上属于一次 DDL 变更,从你之前数据库统一用 PostgreSQL 的背景来看,建议优先用原生分区表而不是中间表切换,同时因为你们内部有‘迁移先同步群’的规则,建议操作前先在群内发个变更计划。”

看到区别了吗?它回答得不再是通用方案,而是贴着你的上下文和流程约束来回答。我认为这是长期记忆最大的价值:让大模型从“万金油”变成“懂行的人”。

3.4 流程三:用户偏好发生变化时怎么覆盖旧记忆

长期记忆还有一个绕不开的场景:用户改主意了。还是同一个用户,过了半年说:“我们现在全面切到 MySQL 了,后面新需求不要用 PostgreSQL 的技术方案了。”

如果记忆系统只能追加不能覆盖,那模型就会同时看到“用户用 PostgreSQL”和“用户用 MySQL”两条矛盾记忆,轻则回答摇摆,重则直接把模型搞懵。DeerFlow 风格的实现里,记忆更新一般不是直接删除旧条,而是采取“版本化”策略。

我在落地时是这样做的:

  • 新条目写入时带一个supersedes字段,指向旧条目的 ID。
  • 旧条目不立即物理删除,而是把状态标记为archived或superseded。
  • 检索时默认过滤掉被新条目覆盖的旧条目,但如果用户显式问“我以前是不是说过我用 PostgreSQL”,工作流可以通过include_archived=true把旧状态也翻出来回答。

这样做的好处是既保持了记忆的时效性,又保留了追溯历史的能力。数据团队做审计或者排查模型“为什么这么回答”的时候,不会因为记忆被物理删除而抓瞎。

4. 长期的实战配置与参数经验

如果说上面还是架构层面的思路,那这一节就是我实际调参过程中沉淀下来的实用经验了。DeerFlow 本身在持续迭代,具体配置项每个版本可能不太一样,但底层那些“长期记忆好不好用”的关键参数,在各家实现里都是相通的。

4.1 向量检索的相似度阈值不要固定死

很多同学会在配置里写死一个 top_k = 5 或者 threshold = 0.7。我踩过几次坑之后,现在会这么调整:

  • 把 top_k 故意调大一点,比如先召回 20 条。
  • 用重排序步骤把相关度最高的 3 到 5 条选出来。重排序可以用一个小的交叉编码器模型,也可以在 DeerFlow 里单独挂一个 LLM 节点做 rerank。
  • 如果某条记忆本身带有高重要度,比如importance=1.0的团队硬约束,即使向量相似度略低于阈值,也应该强制带上。

你看,这就是为什么记忆条目必须要有元数据。没有元数据,你就只能用相似度一个维度做取舍,维度太单薄了。

4.2 时间衰减和生命周期要按记忆类型分开配置

我给自己的系统配了两套衰减策略。对用户偏好类的长期画像,几乎不衰减,除非被新条目显式覆盖。对场景类的临时记忆,比如“用户本周在排查支付回调问题”,我会设一个较短的过期时间,一到两周后逐渐降低权重,一个月后进入可清理状态。

这里给一个参考配置:

记忆类型初始化重要度时间衰减半衰期过期策略
用户画像偏好0.9不衰减被新条目覆盖
团队流程规则1.0不衰减被新条目标记覆盖
项目临时状态0.5~0.77 天30 天归档
对话经验片段0.4~0.614 天90 天归档

按类型区分衰减,而不只是按时间戳一刀切,这样长期记忆才会更接近人类记忆的规律。

4.3 记忆召回和 Prompt 拼接的细节

即使长期记忆工作流已经跑通,召回结果往 Prompt 里拼接的时候,我仍然建议用一个清晰的 XML 式结构把它框起来,而不是直接和用户消息混在一堆。我在 DeerFlow 的 agent 节点里实测下来,下面这种格式最不容易让模型混淆:

<long_term_memory> <item source="..."> <fact>用户后端技术栈是 PostgreSQL</fact> <captured_at>2025-11-01</captured_at> </item> <item source="..."> <fact>数据库迁移改动需要群内同步</fact> <captured_at>2025-11-02</captured_at> </item> </long_term_memory>

在 Prompt 里明确告诉模型:这段内容来自长期记忆,可能是早期记录,必要时要能意识到它已过期。别小看这句话,我见过太多次模型把记忆里的旧事实当成当前硬约束,回答完才发现用户早就换了技术栈。

5. 人机协同、流式输出与可观测性

标题开头那个热词列表里出现了“deerflow 人机协同”和“可观测”这几个词,长期记忆和它们并不是割裂的,正好借这一节串一下。

5.1 记忆流提示与人机协同的配合

在人机协同场景里,长期记忆不只是给模型看的,也要给“人”看。我的做法是,在回复内容之外,额外通过一个 debug 通道往前端推送一条“记忆召回摘要”,告诉用户或开发者:“这次回答引用了你三天前说的 PostgreSQL 偏好。”这种透明的记忆展示,能极大降低用户对“AI 怎么知道得这么清楚”的困惑,也让人工审核人员能快速确认记忆召回是否正确。

DeerFlow 支持把多个节点编排成一个 DAG,在记忆召回和模型回复之间,我建议保留一个可配置开关:如果用户处于“专家模式”,可以让它直接看到原始召回条目和置信度;如果用户只需要结果,就只展示一句“已自动参考历史上下文”。

5.2 结合 SSE 流式接口做记忆写入的时机控制

很多 Agent 都采用 SSE 流式接口交互,DeerFlow 里也逃不开“封装 SSE 流式接口调用逻辑,完成流式消息解析”这件事。在长期记忆这里有一个容易被忽略的点:记忆写入一定要等流式回答结束之后触发,或者至少等到模型端出了done事件再触发。

我之前图省事,想一边流式输出一边做记忆写入,结果经常出现模型生成到最后一步突然自我纠正的情况,而前面的“半成品”已经被写进记忆库了,造成脏数据。现在改为:先把流式输出实时推给前端,但记忆提炼节点只监听流结束事件,再把完整回答交给提炼逻辑去抽取事实。这样虽然多了一次等待,但记忆质量高很多。

5.3 长期记忆的可观测性怎么落地

“可观测”这个词在长期记忆里尤其要落地。AI Agent 的记忆出问题,不像普通服务报错那么直接。你说不清是“没写入”“写错了”还是“召回了不该召回的”。我给的排查链路是:

在记忆写入节点记录memory_written_total指标,标注类型和来源。在记忆召回节点记录memory_recalled_count指标和平均相似度。再把每次写入前后的记忆快照作为 trace span 关联到会话 ID 上。这样一旦用户反馈“它怎么不记得我说过 XXX”,我直接查这个会话 ID 关联的 trace,能看到当时记忆库里到底有没有这条数据、召回时阈值够不够、Prompt 里到底拼没拼进去。

操作上,我会把长期记忆工作流单独抽成一个中间件,并对接 OpenTelemetry。DeerFlow 这类支持 DAG 编排的框架,天然能让每个节点输出 trace span,颗粒度比传统单体对话服务好太多了。

6. 常见问题与排查技巧实录

我把自己在项目里踩过的、以及在社区里看到大家讨论最多的几个长期记忆问题,整理成了一张排查表。这比任何理论都实用。

问题现象可能原因排查方案
模型“假装记得”但回答错误记忆写入节点没成功提炼,或冗余信息和关键信息混在一起打开记忆写入节点的 trace 看最终写入的 memory item 是什么
跨会话后什么都召回不回来向量检索阈值设得过高,或者 query 改写没把关键实体扩写进去下调阈值,检查查询改写环节,人工测试召回 top 10 的内容
模型把过期记忆当成当前状态覆盖机制没生效,旧条目仍在读取范围检查supersedes逻辑,确认已过滤archived条目
记忆库出现同一事实的多个版本没有按事实指纹去重,写入链路并发执行在设计上引入fact_key,同一事实先查重再写
写入过多导致 token 暴涨召回策略太粗,把大量低质量记忆拼进 Prompt增加重排序步骤,限制最终进入 Prompt 的记忆数量
用户画像更新后老接口还在用旧值结构化槽位更新和向量库更新没做事务一致性用消息队列或 DAG 事务节点保证两者都成功再返回

6.1 我踩得最重的一个坑

要说印象最深的坑,还是“无差别写入”。第一版方案为了省事,把每一轮对话都送进记忆提炼节点。结果模型为了表现得更尽职,把大量低价值信息都提炼成了“长期事实”,包括“用户昨天心情不错”“用户提到很忙没时间做饭”。这些信息占满了向量库的召回空间,真正重要的偏好反而不容易排到前面。

后来我加了一道前置过滤节点,先用规则判断这一轮是否包含潜在的长期价值。比如消息里出现了技术栈关键词、项目名、时间节点、流程约束词,才进入提炼节点;否则直接跳过。这一改,召回准确率肉眼可见地提升了。

这里也可以分享一个个人经验:不要贪心。长期记忆系统的能力边界,不是由“能存多少”决定的,而是由“能否在正确的时候忘掉错误的旧信息”决定的。把记忆当成有生命周期的资产去管理,它才会真的对你有用。

6.2 多租户与数据隔离的补充

最后补一句数据隔离。给企业做 Agent 的时候,长期记忆库里绝对不能只有一个裸集合,至少要做到tenant_id + user_id双层命名空间隔离。DeerFlow 示例里可能只写单用户,但实际部署到多团队场景,没有隔离意识的记忆系统等于给自己挖坑。我在每个记忆条目上都会强制带上tenant_id,查询时也必须在过滤条件里带上,防止跨部门串数据。这是合规问题,不只是技术问题。

7. 写在最后的体会

我自己在完整跑通这套长期记忆方案之后,一个非常强烈的感受是:长期记忆能力,最后拼的不是模型能力,而是工程治理能力。模型再聪明,也不会自动替你决定哪些信息该长期记住、哪些过期信息该丢掉、哪些旧版本和更新版本要如何在大脑里共存。这些规则,全部要靠工作流设计来回答。

以后再做智能体项目,我肯定都会按这个思路来设计记忆层:写入走提炼节点,读取走检索加过滤节点,更新走版本覆盖机制,全程接上可观测系统。这套方法论比单独某一行代码重要得多,你甚至可以把它复用到任何框架里。

如果想让记忆层更完善,下一步可以考虑加上周期性记忆总结节点。比如每天凌晨对近期记忆做一次压缩合并,把重复条目归并成一条高置信度的事实。这个动作我试过,效果很不错,既降低存储成本,又减少检索噪音。你可以从简单的“按 user + 时间窗口跑定时任务”开始试。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 23:57:24

储能系统在电力市场中的双重博弈与优化策略

1. 储能参与电力市场的双重博弈&#xff1a;现货与调频的协同优化在电力系统市场化改革进程中&#xff0c;储能系统因其灵活调节特性成为连接电能量市场与辅助服务市场的关键纽带。我们团队基于某省实际市场规则开发的这套决策模型&#xff0c;核心解决的是储能在日前市场中如何…

作者头像 李华
网站建设 2026/10/28 23:57:06

AI记忆技术:从会话上下文到个性化服务的三大支柱

1. 为什么AI需要记住你&#xff1f;上周调试对话机器人时遇到一个典型场景&#xff1a;用户第三次询问"我的快递到哪了"&#xff0c;系统依然要求重复输入单号。这种交互断裂感让我意识到&#xff1a;无法建立持续记忆的AI&#xff0c;永远只是冷冰冰的工具。记忆能力…

作者头像 李华
网站建设 2026/10/28 23:56:36

YOLOv8架构升级:VSSBlock模块提升目标检测性能

1. YOLOv8架构升级背景与VSSBlock引入动机YOLOv8作为目标检测领域的重要里程碑&#xff0c;其架构设计一直保持着快速迭代的节奏。2023年发布的YOLOv8在保持前代实时性优势的同时&#xff0c;通过引入VSSBlock&#xff08;又称MambaLayer&#xff09;这一创新模块&#xff0c;显…

作者头像 李华
网站建设 2026/10/28 23:56:16

Agent Skill不是插件,而是认知外设:契约驱动的集成方法论

1. 这不是给Agent“装插件”&#xff0c;而是重构它的能力生长逻辑最近在几个技术群和开源社区里&#xff0c;反复看到有人发截图&#xff1a;“我给Agent加了10个Skill&#xff0c;结果它反而更不会解题了”“调用天气Skill后&#xff0c;连基础数学推理都崩了”“明明文档写支…

作者头像 李华
网站建设 2026/10/28 23:56:15

MiMo V2.6 开源后训练配方全解析:SFT、DPO 与长上下文

最近开源圈子里讨论最多的一件事&#xff0c;就是 MiMo V2.6 把整套后训练 recipe 放了出来。后训练这三个字看起来很窄&#xff0c;但真正跑过模型训练的人都知道&#xff0c;从 base model 到能用的 chat 模型&#xff0c;后训练占掉了 80% 以上的工程心力。权重谁都能下&…

作者头像 李华
网站建设 2026/10/28 23:55:41

Hybrid-DMKG:大模型知识精准编辑技术解析

1. Hybrid-DMKG&#xff1a;大模型知识编辑的技术革命去年在调试一个行业大模型时&#xff0c;我花了整整三天时间试图修正模型对"晶圆缺陷检测"的专业表述错误。传统微调方法要么需要全量数据重训练&#xff0c;要么像LoRA这样的轻量化方案又难以精准定位知识节点。…

作者头像 李华