Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help


id: ADR-005 title: 内存存储与后端无关,以 SQLite 作为默认实现 date: 2026-07-14 status: accepted relates-to:

  • ADR-002
  • docs/book/src/foundations/fnd-001-intentional-architecture.md
  • docs/book/src/foundations/fnd-002-documentation-standards.md
  • https://github.com/zeroclaw-labs/zeroclaw/issues/6850
  • crates/zeroclaw-api/src/memory_traits.rs
  • crates/zeroclaw-memory
  • crates/zeroclaw-config/src/schema.rs

ADR-005:内存存储采用后端中立设计,默认使用 SQLite

这是对一种在正式 ADR 流程建立之前逐步演变形成的架构的追溯记录。本记录中没有确切的原始决策日期;上方日期是将此 ADR 添加到架构文档中的日期。

FND-002 最初将此决策描述为选择 SQLite 和 Markdown 作为两个记忆后端。该描述已不再能概括更广泛的契约。本记录描述的是持久架构,而不是固化后端数量。

上下文

ZeroClaw 需要在具有不同运行约束的多个安装环境中保持持久内存。本地单进程代理适合使用无需服务依赖的嵌入式存储。运维人员可能还需要人类可读的文件系统存储、共享数据库、向量数据库,或与其他内存系统的集成。

这些存储并不具有完全相同的模式或操作特性。它们仍然需要为记忆操作和作用域管理提供统一的运行时接口。各项操作可能具有特定于后端的能力语义;例如,Markdown 记忆只能追加,不能删除条目。轮次处理不得依赖具体的数据库类型,添加后端也不得要求将提示组装、整合、清理或代理授权策略复制到该后端中。

当前仓库识别 SQLite、Lucid、PostgreSQL、Qdrant 和 Markdown 存储,以及用于禁用持久化内存的 none。SQLite 是默认值。FND-001 单独将 SQLite 和 Markdown 标识为最终最小运行时所需的基线存储;该打包目标并不将存储契约限制为两种实现。

决策

内存持久化通过与后端无关的契约进行选择,默认后端为 SQLite。

存储契约

具体的存储实现了 zeroclaw-api 中的 Memory trait。该 trait 负责与后端无关的持久化操作和条目语义。调用方使用 Memory 句柄,而不是在轮次处理代码中逐一针对 SQLite、Markdown、PostgreSQL、Qdrant 或 Lucid 进行分支判断。

后端构造目前会查询两个存在重叠的配置层级。agents.<alias>.memory.backend 仅直接路由 Markdown 和 none 构造路径,并提供用于同后端共享验证的 kind。所有其他按代理配置的值都会通过安装范围的工厂处理,其中 memory.backend 选择具体类型的 storage.<kind>.<alias> 条目;旧版裸名称会解析为 default 别名。本 ADR 记录了这种交互,但并不将这种重叠视为理想的最终状态。运行时组件必须遵循当前工厂及验证职责的归属,而不是推断不受支持的按代理选择,或创建另一个持久化选择器。

SQLite 仍为默认选项,因为它提供持久化本地存储、混合检索能力,且无需外部服务。当其他后端的存储方式、部署方式、可读性或集成特性更符合需求时,可选用相应后端。none 是显式禁用持久化内存的请求,而非隐式回退。

更改任一选择器都不会迁移现有数据。在不同后端类型之间移动数据需要显式的迁移路径,而不是将一种存储静默地重新解释为另一种。

生命周期策略

存储后端不拥有提示词构建或轮次策略。轮次引擎拥有记忆上下文的选择与渲染。MemoryStrategyMemory 句柄之上用于整合与治理的指定边界,但向该边界的迁移尚未完成:部分路径仍直接调用底层生命周期函数。Issue #6850 跟踪剩余的对齐工作。后端实现提供存储与检索行为,但不会成为这些生命周期规则的长期所有者。

代理范围

记忆独立于具体存储,按智能体身份进行作用域划分。基于 SQL 的存储可能使用内部 UUID,而非 SQL 存储则可能直接使用智能体别名。智能体作用域适配器会将后端绑定到智能体;只有通过配置的允许列表,并且仅当这些智能体使用同一后端时,才允许跨智能体检索。调用方不得绕过这些适配器,也不得推断特定后端的标识符具有相同的表示形式。

此 ADR 不决定哪些后端实现会随特定二进制文件一同发布,也不决定未来的后端是原生实现、通过 feature flag 控制还是由插件提供。这些属于打包和插件生命周期方面的决策。稳定的约束是:每个受支持的后端都必须遵守通用的存储和 agent 作用域契约。

后果

积极后果:

  • Agent、channel、gateway 和工具代码可以依赖同一个内存接口。
  • SQLite 提供了实用的本地默认选项,同时又不会让嵌入式存储成为唯一的部署模式。
  • 运营商可以选择嵌入式、文件支持、共享数据库或向量存储,而无需更改轮次处理的调用方。
  • 提示词的构建、整合和清理可以不断演进,而无需向每个存储实现添加生命周期策略方法。
  • 无论后端采用何种特定的标识符和存储模型,Agent 隔离对调用方都提供统一的可见契约。

负面后果:

  • 后端实现必须保留共享的条目和作用域契约,即使其存储和查询模型不同。
  • 调用方必须考虑功能差异,例如仅追加存储以及报告不受支持或无操作行为的特征操作。
  • 后端类型之间的迁移需要显式的数据迁移;更改已配置的后端不会使现有数据出现在新的存储中。
  • 可选服务和功能会扩大验证矩阵,尽管大多数调用方只会看到共享 trait。

后续决定:

  • Issue #6850 跟踪存储与更高层内存生命周期策略之间的边界。
  • WASM 内存适配器目前还不是可配置的守护进程后端;其运行时构建和打包仍属于独立的插件工作。
  • 对跨后端迁移、共享代理召回或存储标识的更改,都需要进行明确的兼容性评审。

参考文献