ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

DeerFlow DeerMem 移植实战:三步把文件化记忆后端接入另一个 Agent

2026/9/5 20:31:07 拓冰建站 浏览量
DeerFlow DeerMem 移植实战:三步把文件化记忆后端接入另一个 Agent DeerFlow DeerMem 移植实战三步把文件化记忆后端接入另一个 Agent【免费下载链接】deer-flowAn open-source long-horizon SuperAgent harness that researches, codes, and creates. With the help of sandboxes, memories, tools, skill, subagents and message gateway, it handles different levels of tasks that could take minutes to hours.项目地址: https://gitcode.com/GitHub_Trending/de/deer-flowDeerFlowdeer-flow内置的 DeerMem 记忆后端是一个自包含、可移植的模块它只通过一条from deerflow抽象契约导入行与宿主耦合其余全部是包内相对导入。本文基于仓库中的移植样例文档 other_agent_demo/README.md结合源码与可移植性验证测试完整讲解如何把你的 Agent 接入 DeerMem——包括复制文件夹 改一行代码的具体操作、deermem_manager.yaml每个配置项的默认值与取值范围、零配置启动路径以及运行时零 deer-flow 依赖这一契约是如何被自动化测试锁死的。一、前置设计DeerMem 为什么能整体搬走DeerMem 后端位于 backends/deermem/其模块 docstringdeer_mem.py说明了它的分层它把 DeerFlow 记忆机制的五个core/模块storage / queue / updater / prompt / message_processing封装在 backend 中立的MemoryManager契约之后并将 storage / queue / updater / llm 作为PrivateAttr私有依赖在构造时注入依赖注入无模块级单例。这保证了一个实例 一套完整私有状态为整体复制提供了基础。移植的黄金法则写在记忆后端的集成指南 backends/README.md 中后端与宿主之间只有两条通道(1) ABC 方法参数manager.py(2)backend_config字典。后端文件夹中唯一允许的from deerflow导入是name_manager.py中的契约行。在当前仓库中这唯一一行就是 deer_mem.py#L32from deerflow.agents.memory.manager import MemoryConflictError, MemoryCorruptionError, MemoryManager除MemoryManager抽象基类外这里还导入了两个契约级异常MemoryConflictError/MemoryCorruptionErrorDeerMem 用 deer_mem.py 的_call_backend把存储层私有的MemoryRevisionConflict/MemoryStorageCorruption翻译成这对公开异常从而让宿主只需要 catch 契约异常不需要感知 DeerMem 的内部存储类型。这条只允许一行的规则不是口头约定而是被测试持续扫描守护的test_deermem_self_contained.py#L328-L341 的test_portability_only_abc_contract_imports_deerflow会递归遍历整个deermem/包的每个.py文件断言含from deerflow/import deerflow的行恰好只有 1 行且位于deer_mem.py。任何后续改动引入第二处宿主耦合该测试都会立即失败。二、三步接入Vendoring 契约、放入后端、配置加载样例文档给出的接入流程共三步核心承诺是零 deer-flow 代码。第一步Vendor 宿主契约manager.py把agents/memory/manager.py即 manager.py复制到你自己的 Agent 目录树中。样例文档称其为小体积、宿主中立的 9 抽象方法 即插即用工厂契约对照当前源码该模块实际是MemoryManager接口docstring 标注 9 methodsget_memory_manager()单例工厂 后端扫描器_scan_backends()。从源码结构看这 9 个接口方法被组织为三层详见 backends/README.md 的 Backend Contract 一节Tier 1抽象方法必须实现add写入get_context读取注入文本 类方法from_config。缺少任何一个在实例化时即抛TypeErrorpydanticBaseModel的元类继承自ABCMeta见 manager.py#L97-L131 的说明。Tier 2管理操作带默认实现add_nowait默认委托给add、search/get_memory/clear_memory/import_memory/export_memory/delete_memory默认raise NotImplementedError、shutdown_flush默认True。Tier 3可选钩子带默认实现warm默认True无需预热、reload_memory/create_fact/delete_fact/update_fact默认抛错、on_pre_compress/on_turn_start默认 no-op。关键细节MemoryManager不是一个裸 ABC而是 pydanticBaseModel自带backend_config: dict字段校验、modemiddleware | tool字段和跨字段不变量校验例如modetool必须实现search()。这意味着即使你的 Agent 不想要工厂和扫描器最小 ABC 也足够——只要保留 Tier 1 的三个抽象方法、backend_config/mode/callbacks三个字段以及supports_search不变量校验就能直接实例化DeerMem。移植验证测试中内置的这份最小 vendored 契约_VENDORED_MANAGER_PYtest_deermem_self_contained.py#L346-L418就是可直接抄进自己项目的模板。第二步放入后端文件夹改一行导入把backends/deermem/整个文件夹含deer_mem.py与deermem/子包复制到你 Agent 的backends/下然后在deer_mem.py中修改恰好一行# from from deerflow.agents.memory.manager import MemoryManager # to (your agents vendored contract) from your_agent.memory.manager import MemoryManager对应到当前仓库的真实代码完整的一行是from deerflow.agents.memory.manager import MemoryConflictError, MemoryCorruptionError, MemoryManager # 改为以你的 vendored 包 otheragent.manager 为例 from otheragent.manager import MemoryConflictError, MemoryCorruptionError, MemoryManager文件夹内其余所有导入都是相对导入from .deermem.config import DeerMemConfig等见 deer_mem.py#L34-L48不需要做任何其他修改。这也是移植测试实际执行的替换操作见下文第四节。第三步配置并加载放一份deermem_manager.yaml样例见 deermem_manager.yaml然后调用get_memory_manager()工厂或者直接构造DeerMem(backend_config...)。仓库自带的完整样例比文档正文的片段更丰富逐项注释如下manager_class: deermem backend_config: storage_path: ~/.myagent/memory # 留空 $DEERMEM_DATA_DIR / ~/.deermem/ model: # 记忆抽取 LLM留空 不配 LLM provider: openai # 任意 langchain init_chat_model provider model: gpt-4o-mini api_key: ${OPENAI_API_KEY} base_url: https://api.openai.com/v1 debounce_seconds: 30 max_facts: 100 fact_confidence_threshold: 0.7 max_injection_tokens: 2000 token_counting: tiktoken guaranteed_categories: - correction guaranteed_token_budget: 500 staleness_review_enabled: true staleness_age_days: 90 staleness_min_candidates: 3 staleness_max_removals_per_cycle: 10 staleness_protected_categories: - correction配置项详解默认值与取值范围以上每个键都由 DeerMemConfig 解析其默认值和校验范围对移植方尤其重要——因为零配置可运行正是靠这些默认值兜底的键类型默认值范围说明storage_pathstr—数据根目录注意是目录不是文件。空值时取$DEERMEM_DATA_DIR再退回~/.deermem/每用户记忆落在{root}/users/{user_id}/memory.jsonconfig.py#L50-L57storage_classstr—可选的替代存储类点分路径。空值 直接用FileMemoryStorage不走importlib保持可移植modeldict空DeerMemModelConfig—记忆抽取 LLM含provider/model/api_key/base_url/temperature五个子字段model为空 不配 LLM非 LLM 操作照常工作LLM 更新会抛错。宿主工厂可在model为空时注入宿主默认模型host_llm钩子debounce_secondsint301–300队列更新防抖等待秒数queue_max_depthint1000≥0待处理项背压上限0 不限。超限时新的非信号更新被拒QueueFull信号类更新永远放行max_factsint10010–500事实fact容量上限fact_confidence_thresholdfloat0.70–1事实入存的最低置信度max_injection_tokensint2000100–8000记忆注入的 token 预算上限截断由后端自己负责宿主不做预算控制token_countingstrtiktokentiktoken/char计数策略。char为无网络的 CJK 感知估算适合离线环境guaranteed_categorieslist[correction]—无视常规 token 预算、始终注入的事实类别guaranteed_token_budgetint50050–2000保证类别的 token 上限staleness_review_enabledbooltrue—过期事实复审开关staleness_age_daysint9030–365超过该天数的事实成为复审候选staleness_min_candidatesint31–50触发一次复审周期所需的最少过期事实数staleness_max_removals_per_cycleint101–50单周期最多移除的事实数staleness_protected_categorieslist[correction]—豁免复审的事实类别此外还有几个值得移植方知道的隐藏默认行为未知键会告警DeerMemConfig.from_backend_configconfig.py#L328-L353会把不在model_fields里的键忽略并打WARNING例如把storage_path拼成storage_pat会被点名提示避免静默落回默认值、把记忆写到非预期位置。YAML 的null值安全config.example.yaml风格的model:只有注释子项的裸键会被 YAML 解析成None解析时None值会被丢弃并回落到字段默认值有回归测试test_from_backend_config_null_values_fall_back_to_defaults锁定此行为不会触发 pydantic 校验崩溃。回调类钩子不能来自 YAML样例文档特别提醒callbacks/should_keep_hidden_message/trace_context_manager以及host_llm、extraction_callback是纯程序化注入的。宿主工厂通过from_config的关键字参数传入它们见 deer_mem.py 的from_configDeerMem 会把它们合并进DeerMemConfig解析但backend_config字段本身恢复为宿主传入的纯数据不含任何可调用对象保持可序列化。只有在你需要覆盖宿主默认行为时才要程序化设置。三、零配置启动路径不填任何 backend_config 也能跑样例文档承诺 DeerMem 在backend_config完全为空时即可运行这一承诺有明确的默认值支撑和测试背书存储storage_path空 → 依次取$DEERMEM_DATA_DIR环境变量或~/.deermem/每用户数据写在{root}/users/{user_id}/memory.json。测试通过monkeypatch.setenv(DEERMEM_DATA_DIR, ...)指向临时目录来隔离test_deermem_self_contained.py#L30-L36。LLMmodel为空 →build_llm返回Noneconfig.py 的DeerMemModelConfig中modelNone即未配置 LLM。此时import_memory/get_context/get_memory等非 LLM 操作全部可用只有依赖 LLM 的记忆更新会在运行时抛错——而不是启动即崩。LLM 初始化失败优雅降级即使显式配置了model若init_chat_model抛错build_llm捕获异常、打WARNING并降级为None测试test_build_llm_degrades_to_none_on_init_failure记忆 CRUD / 读取 / 搜索照常工作抽取被禁用。存储类storage_class空 → 直接实例化FileMemoryStorage不走importlib测试test_storage_class_empty_uses_filememorystorage。对应的验收测试是test_zero_config_defaults_run_non_llm_opstest_deermem_self_contained.py#L124-L132def test_zero_config_defaults_run_non_llm_ops(deermem_data_dir): dm DeerMem(backend_configNone) # zero config assert dm._llm is None # no model - no LLM dm.import_memory( {version: 1.0, ..., facts: [{id: f, content: x, ...}]}, user_idu, ) assert x in dm.get_context(user_idu) assert dm.get_memory(user_idu)[facts][0][content] x也就是说移植后的最小可用验证就是零配置构造 →import_memory写入 →get_context能读回。四、可移植性证明测试如何锁死零 deer-flow 依赖样例文档的 Proof 一节指向tests/test_deermem_self_contained.py::test_portability_vendor_to_other_agenttest_deermem_self_contained.py#L421-L463。它的流程就是第三节三步的可执行版本建宿主包在tmp_path下创建otheragent/包写入最小 vendoredmanager.py三层 ABC MemoryConflictError/MemoryCorruptionError两个异常类。复制后端shutil.copytree把真实的backends/deermem/复制到otheragent_deermem/。改一行在副本的deer_mem.py中把契约导入替换为from otheragent.manager import MemoryConflictError, MemoryCorruptionError, MemoryManager替换前先用assert contract_import in text确认真实源码里那一行还在——防止有人顺手重构掉契约行导致测试假通过。运行往返把临时目录monkeypatch.syspath_prepend到sys.path最前importlib.import_module(otheragent_deermem.deer_mem)导入零配置构造DeerMem(backend_configNone)执行import_memory→get_context往返断言y in dm.get_context(user_idua)。清理从sys.modules中弹出所有otheragent*模块避免污染后续测试。整条链路不 import 任何 deer-flow 模块运行时零依赖得证。配合test_portability_only_abc_contract_imports_deerflow的全包扫描两个测试从静态只有一行宿主导入和动态拷走即可运行两个方向锁住了可移植性契约。五、移植注意事项清单结合 backends/README.md 的 Common Pitfalls 与 DeerMem 自身实现移植进另一个 Agent 时建议核对以下几点只允许一行宿主导入。不要引入宿主的路径助手如runtime_home、配置单例或模型对象——一切从backend_config读取storage_path由你的工厂注入DeerMem 把它当根目录用配成文件路径会在构造时直接报ValueError见 config.py 的_check_storage_path_is_directory。get_context的长度由后端自己截断。宿主不施加 token 预算DeerMem 通过max_injection_tokens完成token_counting: char可避免tiktoken首次下载 BPE 数据的网络依赖。管理端点返回 DeerMem 形状。get_memory/export_memory等返回的 dict 会被调用方按MemoryResponse形状version/lastUpdated/user/history/facts[]消费如果你改用了非 DeerMem 形状的存储需要写一个小适配器否则数据会被静默丢弃、时间字段为空还会让前端日期格式化崩溃。外部依赖要声明进宿主包管理如pyproject.toml裸装的包会在下次依赖同步时被清理——这是该仓库集成多个外部记忆后端honcho、openviking后总结的通用教训。改配置后重启进程。在 deer-flow 内部MemoryManager是进程级单例不热加载配置移植到自己的 Agent 时同理建议把 DeerMem 实例按进程生命周期管理并在关停前调用shutdown_flush排空防抖队列。背压是后端自己兜底的。DeerMem.add捕获QueueFull并降级为本次更新跳过打 WARNING见 deer_mem.py#L222-L239不会把异常抛回中间件/调用方被丢弃的更新会在下一轮由中间件重新喂入完整对话水线watermark不前进所以不会永久丢失。参考文件移植样例文档backend/samples/other_agent_demo/README.md完整配置样例backend/samples/other_agent_demo/deermem_manager.yamlDeerMem 后端主文件backend/packages/harness/deerflow/agents/memory/backends/deermem/deer_mem.py配置模型backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/config.py宿主契约 工厂backend/packages/harness/deerflow/agents/memory/manager.py记忆后端集成指南backend/packages/harness/deerflow/agents/memory/backends/README.md可移植性与零配置测试backend/tests/test_deermem_self_contained.py【免费下载链接】deer-flowAn open-source long-horizon SuperAgent harness that researches, codes, and creates. With the help of sandboxes, memories, tools, skill, subagents and message gateway, it handles different levels of tasks that could take minutes to hours.项目地址: https://gitcode.com/GitHub_Trending/de/deer-flow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考