首页
/ DeerFlow 的 Honcho 记忆后端:用 HTTP 适配器接入远程用户画像记忆,零本地 LLM 调用

DeerFlow 的 Honcho 记忆后端:用 HTTP 适配器接入远程用户画像记忆,零本地 LLM 调用

2026-09-04 13:27:23作者:傅爽业Veleda

DeerFlow 通过 memory.backend_config 的插件化契约支持多种记忆后端,其中 Honcho 后端是一个"远程-only"的 HTTP 适配器:它把 DeerFlow 的用户维度记忆(长期用户建模、偏好、跨会话工作表征)整体委托给 Honcho(自托管或托管,v3 API),本地不发起任何 LLM 调用——事实抽取与表征构建全部由 Honcho 服务端的 deriver 异步完成。读完本文,你可以掌握:如何配置并切换该后端、每个 backend_config 参数的取值约束与启动校验逻辑、多用户隔离的抗碰撞 workspace 派生机制、写入/召回/检索的完整数据链路、失败策略与异步卸载的实现细节,以及它与 DeerMem 默认后端的能力边界。

定位:覆盖"用户维度"的远程记忆后端

DeerFlow 的记忆后端位于 backends 目录,每个子目录是一个可插拔后端,换后端只需改 config.yaml 一行 memory.manager_class,不需要改动 deer-flow 核心代码。该目录下包含:

  • deermem/:默认后端,deer-flow 自研的结构化事实 + JSON 存储;
  • noop/:空后端,也是新增后端时的模板
  • openviking/:可选远程后端,基于官方 langchain-openviking 包(单用户 middleware 模式);
  • honcho/:本文主角,可选的远程 Honcho HTTP 后端。

Honcho 后端的设计定位(源自其上游 RFC #1898,见 honcho_manager.py 头部 docstring)是:Honcho 负责记忆中的用户维度——长期用户建模、偏好、跨会话的工作表征(working representation)——与面向项目/任务的后端形成互补。摄入(ingestion)本身非常廉价:只是把对话轮次以普通消息写入;真正的"智能"发生在 Honcho 服务端的 deriver 上,它从 add() 写入的消息中异步抽取事实、构建表征。因此该后端本地零 LLM 调用AGENTS.md 中对该后端的总结也反复强调这一点。

后端包内只有四个文件,职责划分清晰:

文件 职责
config.py HonchoConfig 数据类 + backend_config 解析与启动期校验
honcho_manager.py HonchoMemoryManager,实现 MemoryManager 契约
client.py 极简同步 httpx 客户端,直连 Honcho v3 REST API
__init__.py 导出 MANAGER_CLASS = HonchoMemoryManager,供工厂发现

配置:完整参数说明与启动期校验

基本配置

config.yaml(仓库根目录)中启用 Honcho 后端的完整示例如下(继承自 后端 README):

memory:
  enabled: true
  injection_enabled: true
  manager_class: honcho
  mode: middleware            # 或 "tool"
  backend_config:
    base_url: http://localhost:8000
    # api_key: $HONCHO_API_KEY   # 托管 Honcho;plain-http + api_key 需 allow_insecure_http: true
    workspace_prefix: deerflow-u-   # 每个 user id 一个隔离 workspace
    # workspace_overrides: {}    # 将特定 user id 映射到自定义 workspace
    # user_peer_overrides: {}    # 将特定 user id 映射到自定义 peer 名
    assistant_peer: deerflow
    message_char_limit: 8000
    max_injection_chars: 6000
    timeout_seconds: 10
    connect_timeout_seconds: 3
    failure_policy:
      read: fail_open                  # fail_open | fail_closed

参数逐项说明(含源码级取值约束)

以下参数表继承自 backends/README.md 的 Honcho 小节,并结合 config.py 的解析逻辑补充了校验细节:

类型 默认值 说明
base_url str http://localhost:8000 Honcho 实例地址(如 http://localhost:8000https://api.honcho.dev)。解析时会 rstrip("/") 去掉尾部斜杠(见 from_backend_configconfig.py#L65-L88
api_key str 可选 托管 Honcho 的 API key,可写 $HONCHO_API_KEY 环境变量语法;plain HTTP 下使用需 allow_insecure_http: true
allow_insecure_http bool false 允许带 api_key 的明文 HTTP 连接,本地开发用 opt-in;非本地部署应使用 HTTPS
workspace_prefix str deerflow-u- 隔离 workspace 的前缀,最终 workspace 名为 {prefix}{稳定 id}
workspace_overrides dict {} 将特定 user id 精确映射到自定义 workspace;值必须非空,否则解析期报错。多个用户映射到同一 workspace 会共享其搜索索引
user_peer_overrides dict {} 将特定 user id 映射为自定义 peer 名;值必须非空,否则解析期报错
assistant_peer str deerflow 存消息时助手侧的默认 peer 名
timeout_seconds float 10.0 HTTP 调用超时(秒),必须为有限值且 > 0
connect_timeout_seconds float 3.0 建连超时(秒),必须为有限值且 > 0
message_char_limit int 8000 单条消息字符上限,超出截断;必须 > 0
max_injection_chars int 6000 注入系统提示词的记忆字符上限;必须 > 0
failure_policy.read str fail_open 召回失败处理:fail_open(记日志并返回空)或 fail_closed(抛出异常)
storage_path str "" 由宿主注入的只读字段(后端通过 backend_config 接收,不 import deer-flow 路径助手)

源码里这些约束是硬校验而非文档约定:HonchoConfig.__post_init__timeout_seconds / connect_timeout_seconds 要求 isfinite> 0config.py#L53-L63),message_char_limitmax_injection_chars 要求 > 0。注释里特别解释了一个 Python 细节:截断用的是 text[:n]n <= 0 时要么得到空串(n == 0),要么变成负数切片 text[:-1]n == -1 时删掉的是后缀而非上限)——所以负数必须在解析期拦掉。workspace_overrides / user_peer_overrides 的空值校验同样如此:空字符串是 falsy 会静默落到默认派生,YAML null 会被字符串化成名为 "None" 的 id(config.py#L24-L34),两者都会掩盖操作者意图,故 fail fast。测试文件 test_honcho_memory_backend.py 中的 test_rejects_invalid_timeoutstest_rejects_non_positive_character_limitstest_empty_override_values_rejected 等用例逐一锁定了这些边界。

安全姿态与启动行为

两条关键规则:

  1. plain HTTP + api_key 组合默认拒绝api_keyhttp:// 开头的 base_url 时,除非显式设置 allow_insecure_http: true,否则在 from_backend_config 解析期抛 ValueErrorconfig.py#L72-L73),因为 key 会以明文 Bearer header 发送。这与 mem0 后端的姿态一致,是本地开发的显式 opt-in;任何非本地部署应使用 HTTPS。自托管 Honcho 常跑在免认证的明文 HTTP 上,此时不配 api_key 即可正常通过(test_http_without_api_key_is_fine 用例验证)。
  2. 配置错误 fail fast,连通性不探测HonchoMemoryManager.from_config 只解析配置、构造客户端,故意不发起连通性探测(见 honcho_manager.py#L110-L123 的 docstring):一个暂时不可达的 Honcho 不应阻塞 Gateway 启动,读侧随后按 failure_policy.read 降级。

配置变更或后端切换后需要重启 deer-flow——记忆管理器是进程级单例,运行中的进程不会热加载配置或后端代码。

多用户隔离:workspace 派生与抗碰撞设计

这是该后端最值得深入的部分。Honcho 把一切查询限定在单个 workspace 内,因此 DeerFlow 的做法是"每个用户一个 workspace",从构造上保证用户之间看不到彼此的记忆。

派生规则

HonchoMemoryManager._workspace 的解析顺序是:

  1. workspace_overrides[user_id] 精确命中 → 直接用覆盖值(匹配键是原始未清洗的 user id);
  2. 否则 → workspace_prefix + _stable_id(user_id)

关键在于 _stable_idhoncho_manager.py#L75-L88)。Honcho 的 id 语法是 ^[a-zA-Z0-9_-]+$sanitize_id 会把任意字符串映射上去:非 [a-zA-Z0-9_-] 的字符串折叠成单个 -,并截断到 64 字符(config.py#L16-L21)。单靠它有损——"user.name@x""user-name@x" 都会清洗成 "user-name-x",两个不同用户的记忆会被静默合并进同一个 workspace。为此 _stable_id 在可读的 sanitize_id(raw)[:48] 之后追加原始 raw id 的 SHA-256 前 8 位十六进制

digest = hashlib.sha256(raw.encode("utf-8")).hexdigest()[:8]
readable = sanitize_id(raw)[:48].rstrip("-")
return f"{readable}-{digest}" if readable else digest

这样默认路径既可读又抗碰撞;且 digest 恒为 8 位,即使 sanitize_id 把退化的 raw id(如 "!!!")清洗成空串,结果也非空。测试 TestHonchoIdentityDerivation 专门钉死了这一行为:先断言两个冲突的 raw id 在裸 sanitize_id 下确实相等(证明测试真的在演练这个 bug),再断言 mgr._workspace / mgr._user_peer 对它们的输出互不相同。

三条隔离保证

  • 缺失或空 user_id 一律 fail closed:写操作变 no-op(记 debug 日志后直接返回),读操作返回空串/空列表,永远不会落到共享的 fallback workspace
  • 故意共享的 overrideworkspace_overrides 把多个用户映射到同一 workspace 时,是有意共享该 workspace 的搜索索引——search 走 Honcho 的 workspace 作用域 /search(无 peer 过滤),而 get_context / get_memory 仍保持 peer 作用域(只取本人 peer 的表征)。
  • 会话 id 复用同一派生:session id 是 df- + _stable_id(thread_id)。源码注释点明了同级的隐患:"t.1""t-1" 裸清洗后同为 "t-1",若不加哈希后缀,两条线程的历史会合并进同一个 Honcho 会话。测试 test_session_ids_resist_sanitize_collisions 验证两个线程 id 产生两个不同 session。

写入链路:消息过滤到 v3 API

mode: middleware 下,每轮对话由 MemoryMiddleware 触发 add()。从 honcho_manager.py#L155-L192 的实现看,一次 add(thread_id, messages, *, user_id, ...) 做四件事:

  1. 解析身份_workspace(user_id)Noneuser_id 为空 → 直接 return(fail-closed no-op)。用户 peer 取 user_peer_overrides[user_id]_stable_id(user_id);助手 peer 恒为 assistant_peer(默认 deerflow)。
  2. 消息规范化与过滤_content_to_text 把 LangChain 消息的 content(str 或 content-block 列表)归一成纯文本,逐块提取 text 字段;空文本跳过;human 类型消息挂用户 peer,ai / AIMessageChunk 挂助手 peer,tool 等其他类型被丢弃(测试 test_add_maps_messages_to_peers 验证了 "tool" 消息被忽略)。每条消息内容按 message_char_limit 截断(test_add_truncates_long_content 验证超长内容被裁到恰好 N 个字符)。
  3. get-or-create 幂等序列get_or_create_peer(用户)→ get_or_create_peer(助手)→ get_or_create_sessionset_session_peersadd_messages。因为 Honcho 服务端对 peer/session 是 get-or-create 语义,整个序列是幂等的。
  4. fire-and-forget:整个调用包在 except Exception 里,失败仅记 warning 日志后丢弃(at-most-once)。源码注释强调这个 except 必须(catch-all)而非只捕获 HonchoRequestError——客户端任何异常(包括裸 RuntimeError)都不允许逃出 add() 打穿 MemoryMiddleware.after_agent;测试 test_add_swallows_non_honcho_exceptionsRuntimeError 注入验证了这一点。本地不做任何缓冲,这也是 shutdown_flush 返回 True 即 no-op 的原因。

底层 HTTP 端点

client.py 是一个刻意轻量化的同步 httpx 客户端(不依赖官方 honcho-ai SDK),对应六类 v3 REST 调用(client.py#L51-L71):

方法 端点 说明
get_or_create_peer POST /v3/workspaces/{ws}/peers 幂等建 peer
get_or_create_session POST /v3/workspaces/{ws}/sessions 幂等建 session
set_session_peers POST /v3/workspaces/{ws}/sessions/{sid}/peers 绑定会话双 peer
add_messages POST /v3/workspaces/{ws}/sessions/{sid}/messages 批量写入消息
working_representation POST /v3/workspaces/{ws}/peers/{pid}/representation 取 peer 工作表征(max_conclusions 默认 25)
search POST /v3/workspaces/{ws}/search workspace 作用域搜索(无 peer 过滤)

所有传输层失败(非 2xx、非 JSON 的 200 响应——比如前置代理的维护页)统一包装成单一异常类型 HonchoRequestError,让调用方只需处理一种异常;配置了 api_key 时通过 Authorization: Bearer <key> header 携带(测试 test_api_key_headertest_non_json_200_response_wraps_as_honcho_request_error 覆盖)。

召回与检索:两种 mode 的取舍

mode: middleware:无查询的被动召回

get_context(user_id)honcho_manager.py#L195-L206)是 query-less 的:它调用 working_representation 取用户 peer 的工作表征(最多 25 条 conclusions),strip 后按 max_injection_chars 截断,注入系统提示词。user_id 缺失返回空串且不发起任何 HTTP 调用。宿主不会对 get_context 施加 token 预算,长度上限完全靠后端自己截断——这也是 backends/README.md "Common Pitfalls" 第 6 条的要求。

mode: tool:查询感知检索 + 保留被动写入

search(query, top_k=5, *, user_id) 走 Honcho 的 workspace 作用域 /search,返回项被映射成 {content, category, session_id, peer_id, created_at} 结构供 memory_search 工具消费。

这里有一个关键的设计决策:HonchoMemoryManager 声明了类变量 requires_passive_writes_in_tool_mode = Truehoncho_manager.py#L104)。原因是 Honcho 唯一的写入通路是被动的 add()——它的 deriver 从每轮 add() 消息中学习并构建表征,而后端故意不实现 fact CRUD 钩子。如果 tool 模式像某些后端那样停用被动写入,deriver 就断了粮。所以 tool 模式是"被动写入持续喂 deriver + search() 提供模型主动检索"的组合,这与 mem0 后端同一处 ClassVar 的动机完全一致(源码注释与测试 test_requires_passive_writes_in_tool_mode 都点明了这一点)。

get_memory:DeerMem 形状的最小视图

网关与前端目前硬编码到 DeerMem 形状(facts / user / history / lastUpdated)。Honcho 没有 DeerMem 式的事实 CRUD,所以 get_memoryhoncho_manager.py#L234-L258)返回一个最小兼容文档:facts: []user.workContext.summary 填工作表征(同样按 max_injection_chars 截断)、空 history,由网关按契约补默认字段——与 noop 后端 {"facts": []} 的约定相同。

异步执行与失败行为

同步客户端 + 线程卸载

Honcho HTTP 客户端是同步的(httpx.Client),为了兼容 MemoryManager 契约的同步方法签名。DeerFlow 在每个异步边界通过 asyncio.to_thread 卸载它:aadd / aget_context / asearch 三个 a* 方法(honcho_manager.py#L268-L298)只是把同步实现丢进线程池。这样慢的 Honcho 请求永远不会阻塞 ASGI handler 或 SSE 心跳;该目录还配套了阻塞 IO 静态门禁测试 blocking_io/test_honcho_memory_backend.py 确保 httpx 不会跑在事件循环上。close() 释放 HTTP 客户端,是网关的 shutdown hook(测试 test_close_releases_http_client 验证)。

failure_policy.read 的统一闸门

所有召回路径(get_context / search / get_memory)经过同一个 _read_or_fallback 闸门(honcho_manager.py#L138-L152):

  • fail_open(默认):召回失败记 warning 日志,返回传入的 fallback(空串 / 空列表 / 空 DeerMem 文档),运行继续,只是少了一块记忆上下文;
  • fail_closed:把后端错误包成 MemoryManagerError 抛出,沿 prompt 构建链路向上传播并中止本次运行,而不是静默降级——避免向模型和运维掩盖故障(测试 test_search_fail_closed_raises_contract_error 的 docstring 说明了 search 必须与 get_context 同样遵守该策略)。

这个 except Exception 同样是刻意的宽边界:客户端异常不允许逃出进入 MemoryMiddleware.after_agent。测试矩阵对 MemoryManagerErrorRuntimeError 两种注入各做了一组 fail-open / fail-closed 断言,锁死行为。

已知限制

后端 README 的 Limitations 小节,配合源码确认如下:

  • 没有 DeerMem 式的事实 CRUDcreate_fact / delete_fact / update_factimport_memory 及 Settings 页的事实编辑均未实现(继承基类默认 raise,网关捕获 NotImplementedError 后返回 501,前端会看到 Operation 'delete fact' not supported)。get_memory 返回的是最小 DeerMem 形状文档;memory_add / memory_update / memory_delete 工具返回明确的 unsupported-operation 错误。对话写入仍然通过保留的 middleware 完成。DeerMem 仍是默认后端。
  • 没有 DeerMem 存量数据迁移:换后端不会把既有事实搬过去。
  • 写入是每次调用 fire-and-forget:失败的写记日志后丢弃(at-most-once),本地无缓冲,shutdown_flush 是 no-op(honcho_manager.py#L260-L262)。
  • agent_name 未映射:Honcho 建模的是用户而非按 agent 划分的事实,add() / get_context() 签名里的 agent_name 参数被忽略。

可验证的测试锚点

该后端的行为有完整的测试套件钉死,backend/tests/test_honcho_memory_backend.py 的主要分组包括:

  • TestHonchoConfig:默认值、未知键忽略、trailing slash 去除、HTTP+api_key 强制 opt-in、非法超时/字符上限拒绝;
  • TestHonchoClient:六个 v3 端点的路径与 payload 断言(含 peer 绑定 payload)、错误与非 JSON 响应的统一异常包装、Bearer header;
  • TestHonchoManagerWrite:消息到 peer 的映射、无 user 的 no-op(含空字符串)、未映射用户的 deerflow-u- 前缀派生、长内容截断、异常吞噬(含非 Honcho 异常)、list 型 content 归一化、session id 抗碰撞;
  • TestHonchoManagerRead:表征召回、截断、fail-open / fail-closed 两态、get_memory 最小形状;
  • TestHonchoManagerAsync / TestHonchoManagerLifecyclea* 委托、shutdown_flush 恒真、tool 模式可构造、close() 释放客户端;
  • TestHonchoIdentityDerivation / TestFactoryDiscovery:抗碰撞派生、MANAGER_CLASS 可被工厂解析。

另外,客户端构造器接受 transport 参数(httpx.MockTransport 注入),测试无需起真实 Honcho 服务即可验证整条链路。

小结与适用前提

Honcho 后端适合这样的场景:你已有(或愿意自托管一个)Honcho v3 实例,希望由它承担用户画像/偏好/跨会话表征的构建,让 DeerFlow 本地保持"只写消息、只读表征"的轻量角色;多用户部署下靠 per-user workspace 获得构造级隔离。前提与限制是:Honcho 不可达时按 failure_policy.read 降级或中止;没有本地事实 CRUD 与数据迁移;写入 at-most-once。若你需要结构化事实管理、前端事实编辑,DeerMem 默认后端仍是完整选项;本文所有行为描述均基于当前仓库源码与测试,适用前提为 memory.manager_class: honcho 且后端位于 backend/packages/harness/deerflow/agents/memory/backends/honcho/

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384