mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 13:16:24 +00:00
docs(memory): explain the pluggable MemoryManager and how to implement your own backend (#5929)
This commit is contained in:
parent
0f4c05bf28
commit
e080c194e0
@ -38,6 +38,172 @@ Memory has two operation modes:
|
||||
|
||||
In tool mode, the four `memory_*` tool names are reserved. If an MCP server or custom tool already uses `memory_search`, `memory_add`, `memory_update`, or `memory_delete`, the existing tool keeps that name and the colliding memory tool is skipped with a warning.
|
||||
|
||||
## Memory Manager: the pluggable memory manager
|
||||
|
||||
At the core of the memory feature is a **backend-neutral `MemoryManager`** (defined in `backend/packages/harness/deerflow/agents/memory/manager.py`). It abstracts *what memory can do* into a stable contract, while *where memory lives and how it is extracted and retrieved* is left to swappable backends.
|
||||
|
||||
### Design philosophy
|
||||
|
||||
The design goal is a single sentence: **swapping the memory backend requires zero changes anywhere else in DeerFlow**.
|
||||
|
||||
- **The contract says what, not how.** `get_context()` only has to return injection-ready text — whether that comes from a local file load or a remote retrieval call is the backend's own business. The contract does not even assume memory is a set of "facts".
|
||||
- **Tiered by implementation cost.** Only two methods are mandatory (write via `add`, read-and-inject via `get_context`); every other management operation (search, clear, import, fact CRUD, etc.) ships with a default "unsupported" implementation, so a backend exposes exactly as much capability as it implements.
|
||||
- **Fail loud, never silently.** Memory is persistent data. A misconfigured backend or a corrupted file raises directly (e.g. `ValueError` at build time, HTTP 409 on write conflicts) instead of quietly falling back to another store — which would silently write your data to the wrong place.
|
||||
- **Host and backend decoupled.** Host capabilities (Langfuse spans, the default LLM, extraction metrics) are injected as hooks that backends consume as needed, so the backend package itself never depends on a DeerFlow-specific concept and can be shipped independently.
|
||||
|
||||
The `MemoryManager` contract at a glance:
|
||||
|
||||
| Tier | Methods | Purpose |
|
||||
| --- | --- | --- |
|
||||
| Mandatory | `add` / `get_context` | Queue conversations for write (debounced) / return injection-ready memory text |
|
||||
| Management ops | `search`, `get_memory`, `clear_memory`, `import_memory`, `create_fact` / `update_fact` / `delete_fact`, `add_nowait`, `cancel_by_agent`, `shutdown_flush` | Search & inspect, clear & import, fact CRUD for tool mode, emergency flush before summarization, cancel pending extraction, bounded drain on graceful shutdown |
|
||||
| Optional hooks | `warm`, `reload_memory`, `on_pre_compress` / `on_turn_start`, plus async variants | Startup warm-up, cache reload, future extension points |
|
||||
|
||||
### How to use it
|
||||
|
||||
Select a backend via `memory.manager_class` in `config.yaml`; backend-private settings all live under `memory.backend_config`:
|
||||
|
||||
```yaml
|
||||
memory:
|
||||
enabled: true
|
||||
mode: middleware # middleware (default) | tool (model calls memory_* tools directly)
|
||||
manager_class: deermem # backend selector: deermem | mem0 | honcho | openviking | noop
|
||||
backend_config: {} # this backend's own knobs, interpreted by the backend
|
||||
```
|
||||
|
||||
Built-in backends compared:
|
||||
|
||||
| `manager_class` | Type | Positioning |
|
||||
| --- | --- | --- |
|
||||
| `deermem` | Local (default) | The bundled full memory implementation: Markdown fact storage, summaries, full-text retrieval, and fact lifecycle management |
|
||||
| `openviking` | Remote | Connects to an independent OpenViking memory server, recalling through the official `langchain-openviking` adapter |
|
||||
| `mem0` / `honcho` | Remote | Adapters for the Mem0 / Honcho memory products |
|
||||
| `noop` | Local | Empty implementation: memory call sites stay, nothing happens |
|
||||
|
||||
Two things to know:
|
||||
|
||||
- **Tool mode requires search support.** `mode: tool` needs the `memory_search` tool, so the backend must implement `search()`; a backend without it fails at startup rather than silently returning empty results at runtime.
|
||||
- **Custom backends are drop-in.** Create a package under `deerflow/agents/memory/backends/<name>/` exposing `MANAGER_CLASS = <your MemoryManager subclass>`, set `manager_class: <name>`, done. Dotted import paths (`pkg.mod:Cls`) also work.
|
||||
|
||||
### How deermem is integrated
|
||||
|
||||
deermem is the default backend and implements the full contract:
|
||||
|
||||
- The factory `get_memory_manager()` reads `manager_class: deermem` and passes `backend_config` plus a set of **host hooks** (Langfuse tracing callbacks, the default extraction LLM, hidden-message filtering, extraction metrics) to `DeerMem.from_config()`, which assembles the instance.
|
||||
- Storage is bucketed per `(user, agent)`: `memory.json` holds only shared summaries and revision metadata, every fact is its own Markdown file (with YAML front matter), and a SQLite FTS5 full-text index backs `memory_search`.
|
||||
- Writes are queued by `MemoryMiddleware` calling `add()` after each turn (debounced and batched); summarization triggers an `add_nowait()` emergency flush so compacted content is never lost.
|
||||
- On top of that sit opt-in fact-governance capabilities: write-side near-duplicate merging (`fact_dedup_enabled`), staleness review (`staleness_review_enabled`), consolidation (`consolidation_enabled`), capacity eviction policy (`fact_eviction_policy: hybrid-v1`), and relevance-aware retrieval (`retrieval_relevance_enabled`).
|
||||
|
||||
### How openviking is integrated
|
||||
|
||||
OpenViking is a remote memory backend with a crisp responsibility split: **DeerFlow owns capture timing, the recall query, and the transcript cursor; the `langchain-openviking` package owns transport, message conversion, batching, and Session commits.**
|
||||
|
||||
```yaml
|
||||
memory:
|
||||
enabled: true
|
||||
injection_enabled: true
|
||||
manager_class: openviking
|
||||
mode: middleware # middleware mode is recommended
|
||||
backend_config:
|
||||
base_url: http://openviking:1933
|
||||
owner_user_id: default # use default when auth is disabled
|
||||
api_key_env: OPENVIKING_API_KEY
|
||||
failure_policy:
|
||||
read: fail_open # read failure returns empty results (fail_closed aborts)
|
||||
write: log_and_drop
|
||||
retrieval:
|
||||
top_k: 8
|
||||
score_threshold: 0.25
|
||||
max_injection_chars: 12000
|
||||
```
|
||||
|
||||
Integration notes:
|
||||
|
||||
- **One API key is bound to one DeerFlow owner**; accessing another owner is rejected. Keep the USER key in the server environment (`api_key_env`), never in `config.yaml`.
|
||||
- **One DeerFlow thread maps to one stable OpenViking Session**; bounded hash-only cursors live under `{storage_path}/openviking/sessions/`.
|
||||
- This version supports one user with one key in middleware mode; multi-user key provisioning is out of scope. See `docs/OPENVIKING.md` for the full boundary and startup guide.
|
||||
|
||||
## Implementing your own memory backend
|
||||
|
||||
Hooking up your own memory system (a database, an internal service, or a new product) takes four steps — nothing else in DeerFlow changes.
|
||||
|
||||
### Step 1: Create the backend package
|
||||
|
||||
Create a new directory named after your backend under `backend/packages/harness/deerflow/agents/memory/backends/`. As long as its `__init__.py` exposes a `MANAGER_CLASS` attribute (a `MemoryManager` subclass), the factory discovers and registers it automatically — the folder name is the `manager_class` value in config.
|
||||
|
||||
### Step 2: Implement the contract
|
||||
|
||||
Create `mybackend/manager.py`:
|
||||
|
||||
```python
|
||||
from deerflow.agents.memory.manager import MemoryManager
|
||||
|
||||
|
||||
class MyMemoryManager(MemoryManager):
|
||||
# Declare search support (True only if you actually override search();
|
||||
# a mismatch between the flag and the implementation fails at
|
||||
# instantiation, so the two can never drift apart)
|
||||
supports_search = True
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, backend_config, *, mode="middleware", **host_hooks):
|
||||
# backend_config: the dict from memory.backend_config, yours to interpret
|
||||
# host_hooks: optional host-provided capabilities (see below); ignore if unused
|
||||
return cls(backend_config=backend_config, mode=mode)
|
||||
|
||||
def add(self, thread_id, messages, *, agent_name=None, user_id=None, trace_id=None):
|
||||
"""Mandatory: queue conversations for write (filtering/extraction is your private concern)."""
|
||||
|
||||
def get_context(self, user_id, *, agent_name=None, thread_id=None, query=None):
|
||||
"""Mandatory: return injection-ready text (how you retrieve and format is up to you)."""
|
||||
|
||||
def search(self, query, top_k=5, *, user_id=None, agent_name=None, category=None):
|
||||
"""Optional: return facts ranked by relevance (required for tool mode)."""
|
||||
```
|
||||
|
||||
Only `from_config` + `add` + `get_context` are mandatory. Everything else is opt-in with sensible defaults:
|
||||
|
||||
| Capability you want | Method to override | Notes |
|
||||
| --- | --- | --- |
|
||||
| Tool mode (`memory_search` etc.) | `search()`, and set `supports_search = True` | Flag/implementation consistency is enforced at instantiation |
|
||||
| Inspect / clear / import memory | `get_memory()` / `clear_memory()` / `import_memory()` | Unimplemented ops raise "not supported" |
|
||||
| Fact-level CRUD | `create_fact()` / `update_fact()` / `delete_fact()` | Backs `memory_add` / `memory_update` / `memory_delete` in tool mode |
|
||||
| Startup warm-up (indexing, encoders, …) | `warm()`, returning `True` / `False` / `None` | `None` means nothing to warm; the log says "skipped" honestly |
|
||||
| Releasing connections and other resources | `close()` | Called on graceful shutdown |
|
||||
| Cache reload | `reload_memory()` | For caching backends after out-of-band edits |
|
||||
|
||||
### Step 3: Declare failure and conflict semantics
|
||||
|
||||
- Read-failure policy is declared via `read_failures_are_fatal_for_config()`: permissive by default (failures return empty text); setting `backend_config.failure_policy.read: fail_closed` makes callers abort instead of degrading.
|
||||
- Raise `MemoryConflictError` for lost write races (the Gateway maps it to HTTP 409) and `MemoryCorruptionError` for unreadable storage (HTTP 500). Never rely on exception-text matching.
|
||||
|
||||
### Step 4: Enable it
|
||||
|
||||
```yaml
|
||||
memory:
|
||||
manager_class: mybackend # i.e. backends/<folder name>
|
||||
```
|
||||
|
||||
You can also skip the `backends/` directory and use a dotted path directly: `manager_class: mypackage.mymodule:MyMemoryManager`. Either way, a resolution failure raises `ValueError` at startup — no silent fallback.
|
||||
|
||||
### Available host hooks
|
||||
|
||||
`from_config` receives a set of host-default capabilities in `**host_hooks` — consume what you need:
|
||||
|
||||
| Hook | What it does |
|
||||
| --- | --- |
|
||||
| `callbacks` | A `MemoryCallbacks` instance: fired around your LLM calls; the default surfaces memory extraction as a dedicated Langfuse span |
|
||||
| `host_llm_factory` | Host default model factory: use it for zero-config extraction when your backend has no dedicated model |
|
||||
| `should_keep_hidden_message` | Hidden-message filter: by default only messages carrying a human clarification response enter memory |
|
||||
| `trace_context_manager` | Trace context manager |
|
||||
| `extraction_callback` | Extraction metrics callback (token usage, confidence filtering, rejection rates) |
|
||||
|
||||
Consuming none of them is perfectly legal (the `noop` backend uses none).
|
||||
|
||||
### Testing conventions
|
||||
|
||||
Backend-specific tests follow the naming convention `backend/tests/test_<backend>_memory_backend.py`; the existing deermem / mem0 / honcho / openviking tests are good templates.
|
||||
|
||||
## Configuration
|
||||
|
||||
```yaml
|
||||
@ -145,6 +311,8 @@ You can change the storage path with the `storage_path` field. Relative paths ar
|
||||
|
||||
## Custom storage backend
|
||||
|
||||
> This section is about **replacing DeerMem's internal storage layer** (lighter-weight — only changes *where* data lives). To hook up a fully independent memory system, see [Implementing your own memory backend](#implementing-your-own-memory-backend) above.
|
||||
|
||||
The `storage_class` field allows you to replace the default file-based storage with a custom implementation. Any class that extends `MemoryStorage` (`deerflow.agents.memory.backends.deermem.deermem.core.storage`) and implements `load()`, `reload()`, and `save()` methods can be used:
|
||||
|
||||
```yaml
|
||||
|
||||
@ -37,6 +37,171 @@ import { Callout } from "nextra/components";
|
||||
|
||||
在工具模式中,四个 `memory_*` 工具名是保留名。如果 MCP 服务或自定义工具已经使用 `memory_search`、`memory_add`、`memory_update` 或 `memory_delete`,已有工具会保留该名称,冲突的记忆工具会被跳过并记录告警。
|
||||
|
||||
## Memory Manager:可插拔的记忆管理器
|
||||
|
||||
记忆功能的核心是一个**后端中立的 `MemoryManager`**(定义在 `backend/packages/harness/deerflow/agents/memory/manager.py`)。它把“记忆能做什么”抽象成一份稳定的契约,而“记忆存在哪、怎么提取、怎么检索”则交由可替换的后端实现。
|
||||
|
||||
### 设计思路
|
||||
|
||||
设计目标只有一个:**换记忆后端时,DeerFlow 其余代码零改动**。
|
||||
|
||||
- **契约只管“做什么”,不管“怎么做”**。`get_context()` 只要求返回一段可注入提示的文本——是本地文件加载还是远程检索,由后端自行决定;契约甚至不假设记忆一定是“事实”的集合。
|
||||
- **按实现成本分层**。只有两个方法是必须实现的(写入 `add`、读取注入 `get_context`);其余管理操作(搜索、清空、导入、事实增删改等)都带默认的“不支持”实现,后端实现多少能力就暴露多少能力。
|
||||
- **失败要响亮,不要静默**。记忆是持久化数据,配错后端、写坏文件时系统会直接报错(如构建时抛出 `ValueError`、写冲突返回 HTTP 409),绝不悄悄回退到另一个存储——那等于把你的数据写到了错误的地方。
|
||||
- **宿主与后端解耦**。追踪(Langfuse span)、默认 LLM、提取指标等宿主能力以“钩子”形式注入后端,后端按需消费,因此后端代码包本身不依赖任何 DeerFlow 特有概念,可以独立发布。
|
||||
|
||||
`MemoryManager` 契约一览:
|
||||
|
||||
| 分层 | 方法 | 作用 |
|
||||
| --- | --- | --- |
|
||||
| 必须实现 | `add` / `get_context` | 写入对话(防抖排队)/ 返回注入就绪的记忆文本 |
|
||||
| 管理操作 | `search`、`get_memory`、`clear_memory`、`import_memory`、`create_fact` / `update_fact` / `delete_fact`、`add_nowait`、`cancel_by_agent`、`shutdown_flush` | 搜索与查看、清空与导入、工具模式的事实增删改、摘要压缩前的紧急冲刷、取消挂起的提取、优雅停机排水 |
|
||||
| 可选钩子 | `warm`、`reload_memory`、`on_pre_compress` / `on_turn_start` 及 async 变体 | 启动预热、缓存重载、面向未来的扩展点 |
|
||||
|
||||
### 怎么使用
|
||||
|
||||
通过 `config.yaml` 的 `memory.manager_class` 选择后端,后端私有参数全部放在 `memory.backend_config`:
|
||||
|
||||
```yaml
|
||||
memory:
|
||||
enabled: true
|
||||
mode: middleware # middleware(默认)| tool(模型直接调用 memory_* 工具)
|
||||
manager_class: deermem # 后端选择:deermem | mem0 | honcho | openviking | noop
|
||||
backend_config: {} # 该后端自己的参数,由后端自行解释
|
||||
```
|
||||
|
||||
内置后端对比:
|
||||
|
||||
| `manager_class` | 类型 | 定位 |
|
||||
| --- | --- | --- |
|
||||
| `deermem` | 本地(默认) | 自带的完整记忆实现:Markdown 事实存储、摘要、全文检索与事实生命周期管理 |
|
||||
| `openviking` | 远程 | 接入独立的 OpenViking 记忆服务器,通过官方 `langchain-openviking` 适配器回记 |
|
||||
| `mem0` / `honcho` | 远程 | 接入 Mem0 / Honcho 记忆产品的适配器 |
|
||||
| `noop` | 本地 | 空实现:保留记忆调用点但什么都不做 |
|
||||
|
||||
两点使用须知:
|
||||
|
||||
- **工具模式要求后端支持搜索**。`mode: tool` 需要 `memory_search` 工具,因此后端必须实现 `search()`;不支持的后端会在启动时直接报错,而不是运行时静默返回空结果。
|
||||
- **自定义后端即插即用**。在 `deerflow/agents/memory/backends/<name>/` 下创建包并暴露 `MANAGER_CLASS = <你的 MemoryManager 子类>`,把 `manager_class` 设为 `<name>` 即可;也支持填点号导入路径(`pkg.mod:Cls`)。
|
||||
|
||||
### deermem 是怎么接入的
|
||||
|
||||
deermem 是默认后端,完整实现了契约的全部能力:
|
||||
|
||||
- 工厂 `get_memory_manager()` 读取 `manager_class: deermem`,把 `backend_config` 连同一组**宿主钩子**(Langfuse 追踪回调、默认提取 LLM、隐藏消息过滤、提取指标回调)一起传给 `DeerMem.from_config()` 装配实例。
|
||||
- 存储按 `(用户, Agent)` 双维度分桶:`memory.json` 只存共享摘要与修订元数据,每条事实是独立的 Markdown 文件(带 YAML front matter),并维护 SQLite FTS5 全文索引供 `memory_search` 使用。
|
||||
- 写入侧由 `MemoryMiddleware` 在每轮结束后调用 `add()` 排队(防抖批量处理),摘要压缩前通过 `add_nowait()` 紧急冲刷,避免被压缩掉的内容丢失。
|
||||
- 在此之上提供可选的事实治理能力(按需开启):写侧近重复去重(`fact_dedup_enabled`)、陈旧性复核(`staleness_review_enabled`)、合并巩固(`consolidation_enabled`)、容量淘汰策略(`fact_eviction_policy: hybrid-v1`)以及相关性感知检索(`retrieval_relevance_enabled`)。
|
||||
|
||||
### openviking 是怎么接入的
|
||||
|
||||
OpenViking 是远程记忆后端,职责边界划分得很清楚:**DeerFlow 负责捕获时机、召回查询和会话游标;`langchain-openviking` 包负责传输、消息转换、批处理与 Session 提交**。
|
||||
|
||||
```yaml
|
||||
memory:
|
||||
enabled: true
|
||||
injection_enabled: true
|
||||
manager_class: openviking
|
||||
mode: middleware # 建议保持 middleware 模式
|
||||
backend_config:
|
||||
base_url: http://openviking:1933
|
||||
owner_user_id: default # 未开启认证时用 default
|
||||
api_key_env: OPENVIKING_API_KEY
|
||||
failure_policy:
|
||||
read: fail_open # 读取失败时返回空结果(fail_closed 则中断)
|
||||
write: log_and_drop
|
||||
retrieval:
|
||||
top_k: 8
|
||||
score_threshold: 0.25
|
||||
max_injection_chars: 12000
|
||||
```
|
||||
|
||||
接入要点:
|
||||
|
||||
- **一个 API Key 绑定一个 DeerFlow 拥有者**,访问其他用户会被拒绝;USER Key 放在服务端环境变量(`api_key_env`),不要写进 `config.yaml`。
|
||||
- **一个 DeerFlow 线程映射到一个稳定的 OpenViking Session**,会话游标以哈希形式存于 `{storage_path}/openviking/sessions/` 下。
|
||||
- 当前版本支持单用户单 Key、middleware 模式;多用户 Key 分发不在本版本范围内。完整边界与启动指南见 `docs/OPENVIKING.md`。
|
||||
|
||||
## 实现自己的记忆后端
|
||||
|
||||
接入你自己的记忆系统(一个数据库、一个内部服务、或一个新产品)只需要四步,DeerFlow 其余代码一行都不用改。
|
||||
|
||||
### 第 1 步:创建后端包
|
||||
|
||||
在 `backend/packages/harness/deerflow/agents/memory/backends/` 下新建一个以你的后端命名的目录,只要这个包在 `__init__.py` 里暴露 `MANAGER_CLASS` 属性(一个 `MemoryManager` 子类),它就会被工厂自动发现并注册——目录名就是配置里 `manager_class` 的值。
|
||||
|
||||
### 第 2 步:实现契约
|
||||
|
||||
新建 `mybackend/manager.py`:
|
||||
|
||||
```python
|
||||
from deerflow.agents.memory.manager import MemoryManager
|
||||
|
||||
|
||||
class MyMemoryManager(MemoryManager):
|
||||
# 声明支持搜索(只有真正覆盖了 search() 才能设 True,
|
||||
# 两者不一致会在实例化时报错,防止声明与实现脱节)
|
||||
supports_search = True
|
||||
|
||||
@classmethod
|
||||
def from_config(cls, backend_config, *, mode="middleware", **host_hooks):
|
||||
# backend_config: 来自 memory.backend_config 的字典,由你自行解释
|
||||
# host_hooks: 宿主注入的可选能力(见下文),不需要就忽略
|
||||
return cls(backend_config=backend_config, mode=mode)
|
||||
|
||||
def add(self, thread_id, messages, *, agent_name=None, user_id=None, trace_id=None):
|
||||
"""必须实现:把对话排队写入(怎么过滤、怎么提取是你的私事)"""
|
||||
|
||||
def get_context(self, user_id, *, agent_name=None, thread_id=None, query=None):
|
||||
"""必须实现:返回注入就绪的文本(怎么检索、怎么排版由你决定)"""
|
||||
|
||||
def search(self, query, top_k=5, *, user_id=None, agent_name=None, category=None):
|
||||
"""可选:返回按相关性排序的事实列表(工具模式必需)"""
|
||||
```
|
||||
|
||||
必须实现的只有 `from_config` + `add` + `get_context` 三个。其余能力按需追加,全部有合理默认:
|
||||
|
||||
| 想支持的能力 | 覆盖的方法 | 备注 |
|
||||
| --- | --- | --- |
|
||||
| 工具模式(`memory_search` 等) | `search()`,并设 `supports_search = True` | 声明与实现的一致性在实例化时强制校验 |
|
||||
| 记忆的查看 / 清空 / 导入 | `get_memory()` / `clear_memory()` / `import_memory()` | 不实现则调用时报“不支持” |
|
||||
| 事实级增删改 | `create_fact()` / `update_fact()` / `delete_fact()` | 工具模式的 `memory_add` / `memory_update` / `memory_delete` 走这里 |
|
||||
| 启动预热(建索引、加载编码器等) | `warm()`,返回 `True` / `False` / `None` | `None` 表示无事可做,日志会如实记录“跳过” |
|
||||
| 释放连接等外部资源 | `close()` | 优雅停机时调用 |
|
||||
| 缓存重载 | `reload_memory()` | 供带缓存的后端在带外修改后刷新 |
|
||||
|
||||
### 第 3 步:声明失败与冲突语义
|
||||
|
||||
- 读取失败的处理策略通过 `read_failures_are_fatal_for_config()` 声明:默认宽容(失败返回空文本),配置 `backend_config.failure_policy.read: fail_closed` 则让调用方中止而不是降级。
|
||||
- 写冲突请抛 `MemoryConflictError`(Gateway 映射为 HTTP 409),存储损坏抛 `MemoryCorruptionError`(HTTP 500),不要靠异常文本匹配。
|
||||
|
||||
### 第 4 步:启用
|
||||
|
||||
```yaml
|
||||
memory:
|
||||
manager_class: mybackend # 即 backends/<目录名>
|
||||
```
|
||||
|
||||
也可以不放进 `backends/` 目录,`manager_class` 直接填点号路径:`mypackage.mymodule:MyMemoryManager`。无论哪种方式,解析失败都会在启动时报 `ValueError`,不会静默回退。
|
||||
|
||||
### 可用的宿主钩子
|
||||
|
||||
`from_config` 收到的 `**host_hooks` 里带有一组宿主默认能力,按需取用:
|
||||
|
||||
| 钩子 | 作用 |
|
||||
| --- | --- |
|
||||
| `callbacks` | `MemoryCallbacks` 实例:在你的 LLM 调用前后触发,默认实现把记忆提取作为独立 span 呈现在 Langfuse |
|
||||
| `host_llm_factory` | 宿主默认模型工厂:你没有配置专属提取模型时,用它做零配置提取 |
|
||||
| `should_keep_hidden_message` | 隐藏消息过滤:默认只保留带人工澄清回复的消息进入记忆 |
|
||||
| `trace_context_manager` | 追踪上下文管理器 |
|
||||
| `extraction_callback` | 提取指标回调(token 用量、置信度过滤、拒绝率等) |
|
||||
|
||||
不消费任何钩子也完全合法(`noop` 后端就什么都不用)。
|
||||
|
||||
### 测试约定
|
||||
|
||||
后端专属测试遵循命名约定 `backend/tests/test_<backend>_memory_backend.py`,已有 deermem / mem0 / honcho / openviking 的测试可作参考模板。
|
||||
|
||||
## 配置
|
||||
|
||||
```yaml
|
||||
@ -99,6 +264,8 @@ DeerFlow 支持两级记忆:
|
||||
|
||||
## 自定义存储后端
|
||||
|
||||
> 这一节说的是**替换 DeerMem 内部的存储层**(更轻量,只换“存哪”)。如果想接入一个完全独立的记忆系统,请看上面的[「实现自己的记忆后端」](#实现自己的记忆后端)。
|
||||
|
||||
`storage_class` 字段允许你用自定义实现替换默认的基于文件的存储。任何继承 `MemoryStorage`(位于 `deerflow.agents.memory.backends.deermem.deermem.core.storage`)并实现 `load()`、`reload()` 和 `save()` 方法的类都可以使用:
|
||||
|
||||
```yaml
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user