运行时状态和持久化
ZeroClaw 只有一个安装根目录,但没有一个单一的“workspace 数据库”。不同的状态表面有不同的所有者、重载行为和持久性。当变更新增状态、移动状态、缓存配置、涉及重载,或更改 session/memory/log/cost 行为时,请使用这张映射图。
单一事实来源规则仍然适用:如果某个事实已经存在于某个界面中,就不要将其复制到另一个存储字段中。只有当此表标识了拥有该界面时,才存储新状态;否则在使用时从规范拥有者处解析它。
安装布局
对于普通安装,<install> 是解析后的配置目录(默认情况下为 ~/.zeroclaw/,Homebrew 和显式的 --config-dir 安装可以将其移动)。当前布局如下:
<install>/
├── config.toml # canonical user config
├── .secret_key # key for encrypted secrets
├── data/ # instance-wide runtime data
│ ├── sessions/
│ │ ├── sessions.db # default chat/session backend
│ │ └── acp-sessions.db # ACP protocol sessions
│ ├── cron/jobs.db # scheduled job state
│ ├── sop/runs.db # optional durable SOP run state
│ ├── control_plane.db # task supervision records
│ ├── state/
│ │ ├── runtime-trace.jsonl # persisted logs
│ │ └── costs.jsonl # cost ledger
│ ├── devices.db # paired-device metadata
│ └── memory/ # shared instance memory stores
├── shared/ # shared resources, such as skill bundles
└── agents/<alias>/workspace/ # per-agent filesystem sandbox and identity
迁移期间仍然接受旧的 <install>/workspace/ 名称,但新的运行时状态应以 <install>/data/、<install>/shared/ 和每个 agent 的工作区来描述。
状态映射
| Surface | 规范源 | 持久路径 | 内存中的所有者 | 重载 / 并发边界 | 备注 |
|---|---|---|---|---|---|
| 配置值 | zeroclaw-config::Config 已从 config.toml 加载 | <install>/config.toml | 守护进程 Arc<RwLock<Config>> 加上每个子系统解析后的视图 | /admin/reload 会重新读取配置并重新实例化守护进程子系统;直接配置写入会使用模式验证和脏路径检查。RPC 侧的配置变更还会在 RpcContext::config_write_lock 上串行化整个读取-变更-刷新部分(先使用 tokio 互斥锁,再使用 parking_lot RwLock,并且绝不在 .await 或 lock().await 上持有 parking_lot 守卫);网关 HTTP 配置变更同样会在 AppState::config_write_lock 上串行化整个读取-变更-交换部分(锁顺序相同) | 除非缓存会在重载时明确重建,否则不要将从配置派生的事实缓存到生命周期较长的结构体中。 |
| 配置保存持久性 | save() / save_dirty() 在 zeroclaw-config 中的原子写入路径 | <install>/config.toml 以及保留的 config.toml.bak | 与配置值相同 | 写入会依次执行临时文件写入、替换前目录同步、原子重命名以及替换后目录同步 | Ok(()) 表示替换可见,而不表示重命名持久性已得到保证:替换前发生的失败会中止操作,此时磁盘上的配置和当前生效的配置均保持不变;但重命名后发生的目录同步失败仍会返回 Ok(()),同时记录警告并保留 config.toml.bak。在此类保存操作紧接着发生崩溃后,目录项可能会重放为之前的文件;调用方不得将 Ok 视为更强的持久性保证,恢复时可以查阅保留的 .bak。 |
| 加密的密钥 | 配置 secret 字段以及 .secret_key | <install>/config.toml, <install>/.secret_key | zeroclaw-config 中的 secret-store 帮助程序 | Reload 会观察已更改的配置;丢失 .secret_key 会使加密的配置密钥无法恢复 | 切勿将解密后的值复制到日志、文档、PR 正文或运行时元数据中。 |
| Agent 文件系统标识 | 每个代理的工作区文件 | <install>/agents/<alias>/workspace/ | 有效的 SecurityPolicy 和 agent 提示构建 | 在 agent 启动时惰性创建;工作区访问根据 config 进行评估 | 这是文件系统沙箱,不是 providers/channels/tools 的配置权威来源。 |
| 共享技能包 | 已配置的技能包条目和已解析的包目录 | 默认情况下为 <install>/shared/skills/<bundle>/ | 技能加载 / 提示增强 | 重新加载并让新代理在启动时观察配置和文件系统变化 | bundle 别名和目录解析来自配置;文件则是 bundle 内容。 |
| 会话记忆 | 已按代理选择 zeroclaw-memory 后端 | SQLite/Postgres/Lucid/Qdrant/Markdown 后端位置;SQLite 共享存储位于 data/memory/ | Arc<dyn Memory> 包裹在 agent 作用域适配器中 | 一旦某个 agent 写入了数据,后端选择就会被锁定;同一后端的跨 agent recall 需要显式启用 | Memory 行是按 agent 作用域划分的。不要用复制的 prompt/session 缓存来替代 memory 所有权。 |
| 聊天和频道会话 | [channels].session_backend 加 SessionBackend | 默认 data/sessions/sessions.db;旧版/显式 JSONL 使用 data/sessions/*.jsonl | zeroclaw-infra 后端句柄目前由通道、网关、RPC 和会话工具分别构造 | SQLite 后端使用 WAL;SessionActorQueue 按会话串行化活动回合;JSONL 变更共享进程本地的会话目录锁 | Chat/Code 会话使用统一的后端契约。ACP 协议会话使用独立的存储。进程级后端所有权尚未实现单一来源管理。 |
| ACP 会话 | ACP 协议会话存储 | data/sessions/acp-sessions.db | AcpSessionStore 在守护进程启动时以及在 RPC 上下文中打开 | 由 WAL 支持的 SQLite 存储,与聊天会话分离 | ACP session/load 和 session/resume 作用于此协议存储,而不是聊天会话后端。 |
| 实时 RPC/TUI 会话 | RPC SessionStore | 单独为 none | crates/zeroclaw-runtime/src/rpc/session.rs 内存映射 | 进程本地;会话历史仅通过聊天或 ACP 后端持久化 | Live session 句柄、上传、取消令牌、所有者和覆盖项是运行时状态。 |
| Cron 作业 | 声明式配置成员资格加上 cron SQLite 存储 | data/cron/jobs.db | zeroclaw-runtime::cron 调度器/存储 | 读取路径不会创建 jobs.db;调度器拥有到期/锁定状态 | 声明式作业从配置中进行协调,而运行元数据和锁保存在 cron DB 中。 |
| SOP 运行 | SopEngine 加 SopRunStore | 默认情况下为 None;当持久化 SQLite 初始化成功时为 data/sop/runs.db | SOP 引擎活跃/已完成运行缓存 | 持久化存储负责管理准入声明和已持久化的修订版本;引擎在启动时恢复活动状态和终止状态 | 存储初始化失败会记录一条警告,并回退到内存。以内存为后端的审计记录不是运行生命周期的事实来源。 |
| 后台任务监控 | 持久化任务控制平面 | data/control_plane.db | 控制平面句柄、任务生产者和回收器 | 所有者 PID/启动 ID 可识别上次启动遗留的孤儿;心跳超时仅适用于发出心跳的生产者 | 当前的委托/子代理生产者会尽力注册行记录,但会遗漏心跳、父级、路由和主体字段。目标 API 已存在,但端到端的目标执行尚未接入。 |
| 后台委托结果 | 委托结果记录 | <workspace>/delegate_results/<task-id>.json | 委托工具取消注册表和运行中的 future | 结果文件在重启后依然保留;实时取消句柄则不会 | 读取以文件优先,并且仅当文件仍显示为 running 时,才叠加 lost 或 timed_out 监管状态;结果写入和控制平面写入相互独立,可能会出现分歧。 |
| 运行时日志 | zeroclaw-log 事件模式和订阅层 | data/state/runtime-trace.jsonl 在启用持久化时 | 广播 hook、JSONL writer、/api/logs reader、Observer bridge | Rolling/full/none 持久化由配置控制;即使禁用了 JSONL,dashboard SSE 仍会接收事件 | 日志是证据和可观测性,而不是用户配置或会话状态的来源。 |
| 成本分类账 | CostTracker 加上费率配置 | data/state/costs.jsonl | process-global CostTracker | 重新加载会热替换 CostConfig;如果启用了成本跟踪,则会按需构造跟踪器 | 现有记录保持其已记录的价格;费率编辑会在重新加载后影响后续请求。 |
| 网关配对令牌 | PairingGuard 来自 gateway.paired_tokens | 配置中的 token 哈希 | 配对保护 | Reload 会根据配置重建 guard | 有效的 bearer token 是配置状态,而不是 devices.db 行。 |
| 配对设备元数据 | 由令牌哈希键控的设备注册表行 | data/devices.db | DeviceRegistry 缓存加 SQLite | Registry 将元数据与规范的配对标记集进行对账 | 这个 DB 使已配对的设备可见/可管理;它不会生成有效的 token。 |
| 健康状况和组件状态 | 运行子系统报告组件状态 | 无 | 网关健康/状态状态 | 进程本地;在守护进程重启或重新加载时重置/重建 | /health、/api/health 和 /api/status 是当前观测值,而不是持久配置。 |
| 队列、去抖器、看门狗 | zeroclaw-infra 进程实用工具 | 除非调用方将结果存储到其他地方,否则为 none | 内存中的队列/防抖器/看门狗 | 进程内;用于序列化、合并或检测停滞 | 将它们视为协调状态。仅持久化它们所保护的领域数据,而不是队列本身。 |
重新加载并重启
POST /admin/reload 会向守护进程发送一个进程内重载信号。外层守护进程循环会从磁盘重新读取配置并重新运行守护进程,根据新配置创建新的网关、通道、心跳、调度器、MQTT、会话、内存和成本连接。PID 保持不变,但监听器会短暂重新绑定。
完整的进程重启还会轮换进程本地状态,例如活动 RPC 会话、健康快照、actor 队列,以及任何临时的工具收据密钥。持久化存储会根据上表在重启后保留。
会话后端迁移
选择 SQLite 会话后端时,构造后端句柄会导入旧版 data/sessions/*.jsonl 文件。导入器在持有进程本地 JSONL 变更锁的同时,将每个源文件移至私有的 .jsonl.importing 版本,在一个 SQLite 事务中写入消息、元数据和与源文件绑定的导入回执,然后将源文件保留为 .jsonl.migrated 以便回滚。
回执将源文件名、会话键、SHA-256 摘要和字节长度绑定在一起。回执事务开始前,导入器会同步暂存的源文件,并在 Unix 上同步活动目录到暂存目录的重命名。迁移事务会在导入提交时使用完整的 SQLite 同步,然后恢复正常的运行时设置。归档移交同样会在 Unix 上同步目录元数据,然后移除暂存的源文件。在扫描源文件之前,后端构造会根据持久化回执恢复进程本地的非活动状态。导入回执一经提交,即使归档移交或后续文件失败,该会话目录的 JSONL 变更仍会保持非活动状态。下一次构造可以根据该回执验证暂存源,并完成移交,而不会插入重复消息。空的和仅包含空白字符的 JSONL 文件会作为不含消息的 SQLite 会话保留;不含有效消息的非空源文件仍会安全失败。
在不导入源的情况下构建 SQLite 后端,并不会停用 JSONL 变更。因此,如果不存在持久化的导入回执,进程内重新加载可能会切回 JSONL。
没有回执的源不会与同一会话键对应的现有 SQLite 消息或元数据合并。如果提交前检查失败,则会将暂存源恢复到其实际的 JSONL 路径。不兼容的回执、暂存源或归档会导致后端构造返回错误。当前,每个进程入口点都会独立决定该错误是停止子系统,还是禁用持久化;进程级所有权和启动策略与迁移契约彼此分离。
备份和恢复
对于普通的单实例安装,请备份整个 <install> 目录。至少应包括:
config.toml.secret_key如果使用了加密机密data/memory/data/sessions/data/cron/jobs.db如果 cron jobs 通过运行时 surface 配置- 如果启用了持久化 SOP 运行,则为
data/sop/runs.db data/control_plane.db(如果监督任务历史记录很重要)data/state/costs.jsonl如果成本历史很重要data/state/runtime-trace.jsonl如需用于事件审查的日志data/devices.db用于已配对设备元数据
不要让两个守护进程针对同一个安装根目录运行。多个存储使用具有单写入者模型的 SQLite,而进程本地缓存假定只有一个守护进程拥有该实例。
源指针
- Config、install-root 和 data-dir 的解析:
crates/zeroclaw-config/src/schema.rs - 会话后端:
crates/zeroclaw-infra/src/session_sqlite.rs、crates/zeroclaw-infra/src/session_store.rs - ACP 会话存储:
crates/zeroclaw-infra/src/acp_session_store.rs - RPC 实时会话:
crates/zeroclaw-runtime/src/rpc/session.rs - Cron 持久化:
crates/zeroclaw-runtime/src/cron/store.rs - SOP 持久化:
crates/zeroclaw-runtime/src/sop/store/ - 后台任务与目标监督:
crates/zeroclaw-runtime/src/control_plane/ - 后台委托结果:
crates/zeroclaw-runtime/src/tools/delegate.rs - 日志:
crates/zeroclaw-log/ - 成本账本:
crates/zeroclaw-config/src/cost/tracker.rs - 配对守卫:
crates/zeroclaw-config/src/pairing.rs - 设备注册表:
crates/zeroclaw-gateway/src/api_pairing.rs - 重新加载端点:
crates/zeroclaw-gateway/src/lib.rs