DeerFlow 长期记忆:用户画像与跨会话注入
昨天说了「前端、TypeScript、报告用中文」,今天再要一份技术报告时,没有记忆的 agent 会把语言和篇幅再问一遍。DeerFlow 的 Memory 在回合结束后异步提炼事实,写入 memory.json,下次开聊注入模型可见、界面默认不可见的提醒,让连续使用时少重复交代背景。
memory.json:摘要、facts、confidence
持久化结构大致三层。user 存工作语境、个人偏好、当前关注等摘要段落(workContext / personalContext / topOfMind);history 存近中远时间线摘要;facts 是可增删的具体条目,带 id、content、category、confidence、createdAt、source 等字段。分类包括 preference、knowledge、context、behavior、goal、correction。摘要适合概括画像,facts 适合可量化、可淘汰的细节。
confidence 约定大致是:0.9–1.0 明确声明(「我是前端工程师」),0.7–0.8 强暗示(一直在聊 React),0.5–0.6 弱推断。写入时低于 fact_confidence_threshold(常见默认 0.7)的条目不入库,减少「我可能想学 Rust」这类随口一说污染画像。存储还可分用户级(users/{user_id}/memory.json,该用户各 agent 共享)与 agent 级(.../agents/{agent_name}/memory.json);使用某 agent 时可两套一起注入。
写入:Middleware、防抖队列、Updater
MemoryMiddleware.after_agent 在记忆功能开启时取 thread_id 与消息,用 filter_messages_for_memory 丢掉工具调用噪音,只留用户输入与最终回复,再 MemoryUpdateQueue.add。工具调用是过程噪音,对理解用户帮助有限,还会浪费更新提示词的 token。
队列用 debounce_seconds(默认约 30 秒)防抖:有新对话就重置计时,空闲后再批处理,避免每轮立刻打 LLM,也避免连续刷消息时重复更新。计时触发后 MemoryUpdater.update_memory 读取当前记忆,把对话格式化进更新提示词,让模型返回结构化更新。解析侧从回复文本里抠 JSON,容忍前后废话或代码块包裹。模型有时会加「好的,分析结果如下」之类前缀。
应用更新时:user 各段看 shouldUpdate;factsToRemove 删过期 id;newFacts 过置信度再追加;超过 max_facts(常见默认 100)则按置信度降序截断。落盘先写带随机后缀的临时文件再 replace,避免写到一半崩溃留下半成品文件。
还会做几层卫生:内容键去重(大小写无关);用正则清掉上传文件类临时叙述(会话级路径下次可能不存在,留下记忆会误导 agent 去找不存在的文件);detect_correction 抓「不对」「别用 X 用 Y」「你搞错了」一类信号,Updater 用高置信度提示强化 correction 事实,并可带 sourceError 记录旧错误做法。注入时 correction 可格式化为带 (avoid: …) 的行。
1 | class MemoryUpdateQueue: |
注入、token 预算、人工管控
DynamicContextMiddleware 在 before_agent 注入。首轮拼完整 <system-reminder>(记忆块 + <current_date>);之后主要处理跨天日期更新。提醒做成隐藏 HumanMessage(hide_from_ui),并用消息 ID 技巧插在首条用户消息前:提醒消息复用原用户消息 id,用户消息改派生 id,便于在 LangGraph 消息追踪下做位置替换,也利于前缀缓存(frozen-snapshot:首轮注入后尽量稳住前缀)。模型看到的是「隐藏提醒 + 可见用户句」,用户界面默认只看到后者。
注入有 max_injection_tokens(常见 2000):先放 user/history,再按置信度填 facts,超预算就停。即使库里有上百条 facts,也只有最重要的一批进上下文。token 计数优先 tiktoken,不可用则字符估算,并对 CJK 比例做区分(中文约 1.5–2 字符/token,英文约 4),避免弱网下载 BPE 卡住。主方案失败时降级,属于有意为之的健壮性。
HTTP API 提供 fact 的增删改查、整包 /memory 读写、import/export,以及清空。你可以手动补一条「下周出差」、删掉记错的偏好、导出备份换机器,或一键忘记。自动提炼之外,用户可以改错记、备份迁移。记忆的最终控制权在用户手里。
两天之间发生了什么
第一天用户交代「前端、TypeScript、中文报告」。对话结束后 after_agent 入队,约三十秒无新消息后 Updater 调 LLM,把偏好写进 memory.json。第二天用户只说「写一份 Vue 相关技术报告」:before_agent 读出记忆,注入类似「Work / Personal / Facts」的 <system-reminder>,模型按既有格式写,用户不必重讲一遍。
注入片段对用户隐藏,对模型可见,大致包含用户语境摘要与带类别、置信度的 facts 列表,再附当天日期。写路径是 MemoryMiddleware → MemoryUpdateQueue → MemoryUpdater → memory.json;读路径是开聊时 DynamicContextMiddleware 读文件并注入。连续对话里少问一遍背景,靠的是这套防抖异步提炼加受控注入。
DeerFlow 长期记忆:用户画像与跨会话注入