FND-001:有意架构设计:ZeroClaw 微内核迁移
起始版本 v0.7.0 · 类型:架构 · 修订版 10
规范参考 · 经团队批准 · 修订版 10 原始 RFC 讨论和草稿历史:#5574
在你们阅读此内容之前,给团队的一条提示。
本文档旨在帮助我们将一个被动式增长的代码库,转变为一个有意识、有规划地构建的代码库。如果你对这里的某些概念感到陌生,这并不是问题,这恰恰说明本文档发挥了它应有的作用。每一位与你共事的资深工程师,都是在一个庞大到难以理解的代码库上,通过艰难的经历才领悟到这些道理的。而我们拥有一个难得的机会,能够及早识别出这种模式,并在它带来痛苦之前及时纠正方向。这是一件好事。请慢慢体会。
目录
修订历史
| 修订 | 日期 | 摘要 |
|---|---|---|
| 1 | 2026年4月9日 | 初始草稿 |
| 2 | 2026年4月9日 | 新增 §4.4.1 版本策略(统一工作区继承、稳定性层级、产品级破坏性变更定义);新增 §4.4.2 发布制品(特性标志的命运、标准发布二进制文件配置、发布制品矩阵);新增关于版本策略和可观测性默认值的讨论问题 |
| 3 | 2026-04-10 | 根据 PR #5559 的实施反馈进行术语修正:在代理编排层中,将“kernel”统一改为“runtime”;“kernel”现在特指不可简化的基础层(--no-default-features 构建);§4.1 更新为描述明确的两层架构(基础层 + 运行时);§4.2–§4.3 的依赖图和组件映射已更新,以显示 zeroclaw-runtime;Phase 2 从“The Kernel”重命名为“The Runtime”;二进制大小目标被重新定义为具有衡量进展跟踪的愿景性北极星指标,而非硬性门槛;§7 更新为包含实际的 Phase 1 测量结果(6.6 MB 基础层构建),并明确指出架构分解有助于优化,但优化是独立的第二轮工作。 |
| 4 | 2026-06-02 | 将 §5.2 更新为以 wasm32-wasip2 为目标,以启用 WIT 文件。更新第 2 阶段 §D2,用 wasmtime 替换 Extism,以启用 ARM32 目标和 WIT 文件。 |
| 5 | 2026-06-29 | 修订了 §4.4.2,将单个始终启用的 plugins-wasm 行替换为三个标志的执行后端分类(plugins-wasm 主机加上 plugins-wasm-cranelift / plugins-wasm-pulley 后端),完成了 RFC #6943 的去冲突处理 |
| 6 | 2026-06-30 | 已从发布构件矩阵、目标架构、路线图和成功标准中移除桌面安装程序(#8544)。 |
| 7 | 2026-07-04 | 恢复了桌面安装程序,以及其在发布、架构、路线图和成功标准方面的要求(#8565)。 |
| 8 | 2026-07-20 | 将根目录的 AGENTS.md 设为精简的项目契约,通过架构地图和编码代理指南承载维护细节,并防止 crate 策略削弱项目的安全性、隐私或授权要求(#9050)。 |
| 9 | 2026-08-11 | 在该渠道于 #9571 中退役后,已从当前状态的网关清单和 v0.9.0 插件迁移目标中移除 WATI;通用 webhook/plugin 边界保持不变 |
| 10 | 2026-08-19 | 在 #9853 中这两个 crate 退役后,从工作区继承和独立发布指南中移除了 aardvark-sys 和 zeroclaw-robot-kit;已发布的 0.1.0 版本仍保留在 crates.io 上,不受影响 |
此规范文档中的修订号遵循已批准的仓库历史记录。链接的 RFC issue 还将一项配置规范编辑标记为草案 Rev. 4,但在此基础文档通过 #5911 批准时,该文本并未包含在内。当前的配置权威来源和环境覆盖行为记录在配置生命周期和环境变量中。
1. 开发理念:愿景优先
我们在软件领域做出的每一个决策——构建什么、如何构建、跳过什么——都应当自上而下地从意图的层级结构中流淌而出:
Vision
└── Architecture
└── Design
└── Implementation
└── Testing
└── Documentation
└── Release
这不是瀑布式流程,而是一个决策层级。这意味着在编写函数时,你应该能够向上追溯一条清晰的链路:这个函数之所以存在,是因为某个设计决策;该设计决策之所以存在,是因为某个架构选择;而该架构选择之所以存在,是因为某种愿景。如果你无法画出这条链路,那么这段代码可能就不应该存在。
各层在实际中的含义:
| 层 | 它回答的问题 | 如果没有它,会发生什么问题 |
|---|---|---|
| 愿景 | 为什么会有这个项目?它面向谁?成功的标准是什么? | 你构建的东西没人需要,或者在不同版本之间自相矛盾。 |
| 架构 | 哪些结构决策使这一愿景成为可能? | 你最终会得到一个“大泥球”:代码虽然能运行,但牵一发而动全身,无法修改。 |
| 设计 | 组件之间是如何关联的?它们之间的接口是什么? | 你会得到紧耦合:各个组件对彼此的内部实现了解得过多 |
| 实现 | 如何构建这个特定组件? | Bug、性能问题、安全漏洞 |
| 测试 | 实现是否符合设计?设计是否服务于架构? | 你发布了有问题的东西,但不知道为什么 |
| 文档 | 我们如何将这一知识传递给下一个人? | 每位贡献者都必须从头开始重新发现一切 |
| 发布 | 我们如何安全且可持续地将此功能带给用户? | 用户会遇到损坏或令人困惑的软件 |
跳过顶部的弊端
ZeroClaw 由 AI 工具基于 OpenClaw 的 TypeScript 代码库引导生成。AI 代码生成工作在实现层进行。它编写执行具体功能的函数、结构体和模块。它不设定愿景,不做架构决策,也不定义设计契约。
最终得到的代码库功能完善得令人惊叹,但架构却纯属偶然。这些代码满足了当下的需求,却并非经过设计,而是逐渐堆积而成。这种模式在我们这个行业有个专门的名字:大泥球(Big Ball of Mud)。它是软件领域最常见的架构,并非因为有人主动选择了它,而是当你跳过层级结构顶端时必然得到的结果。
此 RFC 是我们修复该问题的契机,但方法不是抛弃那些行之有效的部分,而是采用一种称为 Strangler Fig Pattern 的技术,围绕其构建有意识的架构:我们在旧结构的边缘搭建新结构,随时间推移逐步向内迁移,直到旧结构消失。无需“一步到位“的重写,无需抛弃可用的代码。只需稳步、有意识地改进。
2. 愿景:ZeroClaw 是什么
在讨论架构之前,我们需要明确我们正在构建的内容。这就是愿景层(Vision layer)。后续的所有内容都必须服务于这一目标。
ZeroClaw 是一个个人 AI 助手运行时,任何人都可以在任何硬件上运行它,从价值 10 美元的嵌入式开发板到云服务器皆可,无需任何配置开销、无需任何外部服务依赖,并且在功能或安全性上毫不妥协。
将其分解为具体的承诺:
零开销。 核心 agent 在数毫秒内即可启动,占用的内存比一个浏览器标签页还少。这并非营销噱头,而是一项架构约束。我们做出的每一个决策都必须经受这一标准的检验。
零外部依赖。 用户下载 ZeroClaw 并配置好 LLM 提供商后,无需安装任何其他东西,就应该拥有一个可用且实用的 AI 助手。频道、仪表盘和各类集成是你需要时才添加的功能,而不是它能工作之前必须具备的前提。
零妥协。 Lean 并不意味着弱。ZeroClaw 必须具备严谨的安全模型、真实的可观测性以及真正的可扩展性。通过组合来解决“小二进制文件”与“完整功能”之间的张力:一个小型核心,通过你选择的组件进行扩展。
为每一种技能水平而设计。 无论是使用价值 10 美元的 Raspberry Pi 的学生,还是运行生产环境部署的团队,都应该感觉 ZeroClaw 是为他们量身打造的。这意味着默认体验必须简单,而进阶体验必须强大,二者并非两款不同的产品。
用户自有。 您的数据、您的硬件、您的配置。ZeroClaw 不需要账户,不会向外部发送数据,也不会将您锁定在某个平台上。
3. 诚实评估:我们目前的状况
本节并非对任何人的工作进行批评。这是一次诊断,而你不指出问题所在,就无法解决问题。
3.1 结构问题
整个 ZeroClaw 代码库目前都位于一个 Rust crate 中。这意味着:
- 无论你是否使用 Telegram,Telegram 频道和核心代理循环都从同一个源代码树中编译
- Web 仪表板(一个完整的 React 应用程序)通过
rust-embed嵌入到二进制文件中,使得每个二进制文件都包含 Web UI,即使对于仅使用 CLI 的用户也是如此。 - 网关 HTTP 服务器包含 WhatsApp、Linq、Nextcloud Talk 和 Gmail 的 webhook 处理程序,这意味着特定的渠道集成已内置于 Web 服务器中
- 所有 70 多个工具都被编译到二进制文件中,无论用户是否会调用这些工具。
- 排除代码的唯一机制是 Cargo 特性标志,这要求用户具备 Rust 开发环境并从源代码重新编译。
对用户的影响: 既定目标是为价值 10 美元的硬件打造一个精简的二进制文件。但该二进制文件却附带了 27 个消息通道、70 多个工具、一个完整的 Web 服务器、一个 React 应用,以及与 Jira、Notion、Google Workspace、LinkedIn 等的集成代码,而其中大部分都是任何特定用户永远不会用到的。
对贡献者的影响: 当一个文件长达 9,500 行时,就无法理解它。当所有功能都集中在一个 crate 中时,修改任何内容都有可能导致整个系统崩溃。
3.2 证据
这些是从当前代码库中得出的测量事实,而非估算值:
| 文件 | 行 | 它的作用 | 它应该做什么 |
|---|---|---|---|
src/agent/loop_.rs | 约 9,500 | 工具调用解析、流式处理、历史记录、成本跟踪、模型路由、记忆、凭证清理、上下文构建 | 编排单个智能体回合 |
src/gateway/mod.rs | ~2,260 | Web 服务器 + React 应用服务器 + WhatsApp Webhook + Linq Webhook + Nextcloud Webhook + Gmail Webhook + 配对 + 速率限制 + WebAuthn | 提供 Web 仪表板 API |
src/providers/mod.rs | ~3,750 | 工厂 + 40+ 个提供程序实现 + OAuth 流程 + 凭据解析 + 错误清理 | 路由到提供程序 |
src/tools/mod.rs | all_tools_with_runtime() 在第 387–1066 行 | 无条件实例化所有 70 多个工具 | 注册用户配置的工具 |
一个 9,500 行的文件不是一个模块。它是一个碰巧带有 .rs 扩展名的单体。
3.3 已经做得好的部分
这一诊断不应掩盖真正设计良好的部分:
- 特质层非常出色。
Provider、Channel、Tool、Memory、Observer、RuntimeAdapter和Peripheral是清晰且文档完善的 Rust 特质。这些正是正确的分层点。问题在于它们并未对应 crate 边界,因此编译器无法强制执行分层结构。 - WASM 插件系统已部分构建完成。
PluginHost、WasmTool、WasmChannel、PluginManifest以及 Ed25519 签名验证均已存在于src/plugins/中。执行桥接器目前为存根实现,但整体结构正确。 - 可观测性系统已成熟。 OpenTelemetry、Prometheus 和 DORA 指标均基于一个清晰的
Observertrait 实现。这是生产级质量的工作。 - 安全模型设计周到。 配对码、自主级别、沙箱机制以及策略执行都体现了明确的设计意图。
我们不是在重写 ZeroClaw。我们是在为它已有的优秀理念提供一个可以成长的框架。
4. 目标架构
4.1 微内核模型
微内核架构将一个最小化、稳定的核心与扩展它的可选子系统分离开来。在操作系统中,经典的例子是一个仅处理内存和调度的内核,而其他所有功能——文件系统、设备驱动程序、网络协议栈——都作为独立进程运行,并通过定义良好的接口进行通信。
对于 AI 代理运行时,该映射揭示了 两个不同的内部层,而操作系统类比将它们混为一谈:
| 操作系统微内核概念 | ZeroClaw 等效 |
|---|---|
| 内核 | 基础层:API trait、配置、provider、内存后端、基础设施、工具调用解析器。不可再简化的核心:可通过 --no-default-features 构建。能够与 LLM 交换消息并存储内存。仅此而已。 |
| 初始化 / 运行时系统 | Agent 运行时层:编排循环、安全策略强制执行、插件宿主、核心工具、IPC API。zeroclaw-runtime crate,由 agent-runtime 特性控制启用。正是它让 ZeroClaw 成为一个 agent,而不仅仅是一个库。 |
| IPC | 运行时与外部组件之间的本地套接字 / IPC API |
| 设备驱动程序 | 频道插件(Telegram、Discord 等) |
| Filesystem 驱动程序 | 内存后端插件(SQLite、Markdown) |
| 用户进程 | 网关二进制文件,Tauri 桌面应用 |
这一区别很重要:基础是任何 ZeroClaw 二进制文件正常运行所必须存在的最小条件。运行时是它作为代理(agent)正常运行所必须存在的最小条件。其余部分都是组合而成的。
这一双层拆分是在阶段 1 工作区分解过程中确定的(PR #5559),并反映在 crate 命名中:zeroclaw-runtime(crate)由 agent-runtime(特性)进行门控。本 RFC 的早期版本中,“kernel”一词被松散地用于指代现在正确命名为 runtime 层的组件。本修订版在整个文档中更正了这一术语。
4.2 依赖规则
这个设计中最重要的架构规则——一旦违反就会导致整个结构崩塌的规则——就是:
依赖关系向内流动。运行时对插件一无所知。插件了解 API。没有任何东西了解所有东西。
zeroclaw-api ← defines all traits (Provider, Channel, Tool, ...)
▲ no implementations, no heavy dependencies
│ depends on
foundation crates ← zeroclaw-config, zeroclaw-providers, zeroclaw-memory,
▲ zeroclaw-infra, zeroclaw-tool-call-parser
│ depends on all depend on zeroclaw-api; no cross-dependencies
zeroclaw-runtime ← implements the agent loop (agent-runtime feature)
▲ depends on zeroclaw-api + foundation crates
│ depends on knows nothing about specific channels or tools
plugin crates ← zeroclaw-channel-discord, zeroclaw-tools-web, ...
▲ depend on zeroclaw-api (not the runtime)
│ depends on
zeroclaw binary ← thin wiring layer
reads config, registers plugins, starts runtime
如果 zeroclaw-runtime 导入了 TelegramChannel,则架构已被违反。一旦定义了 crate 边界,编译器将强制执行此规则。
4.3 组件映射
┌─────────────────────────────────────────────────────────────────────┐
│ zeroclaw (binary crate) │
│ Reads config → registers only configured components → starts │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ zeroclaw-runtime (agent-runtime feature) │ │
│ │ │ │
│ │ Agent Loop · CLI Channel · Security Policy │ │
│ │ Plugin Host · Local IPC API │ │
│ │ Core Tools: shell, file, git, memory recall/store │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ Foundation (--no-default-features) │ │ │
│ │ │ │ │ │
│ │ │ zeroclaw-api · zeroclaw-config · zeroclaw-infra │ │ │
│ │ │ zeroclaw-providers · zeroclaw-memory │ │ │
│ │ │ zeroclaw-tool-call-parser │ │ │
│ │ │ │ │ │
│ │ │ Vision target: <5 MB RAM at runtime │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ ▲ │
│ zeroclaw-api (traits only) │
│ ▲ │
│ ┌──────────────┐ ┌────────┴────────┐ ┌─────────────────────┐ │
│ │ zeroclaw-gw │ │ Channel plugins│ │ Tool plugins │ │
│ │ (opt-in │ │ │ │ │ │
│ │ binary) │ │ channel-discord│ │ tools-web │ │
│ │ │ │ channel-slack │ │ tools-integrations │ │
│ │ HTTP/WS/SSE │ │ channel-tg │ │ tools-hardware │ │
│ │ Web UI │ │ channel-email │ │ tools-mcp │ │
│ │ REST API │ │ ... │ │ ... │ │
│ └──────┬───────┘ └─────────────────┘ └─────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ zeroclaw-desktop│ ← Tauri app (already exists in apps/tauri) │
│ │ System tray app │ bundles zeroclaw-gw as a sidecar │
│ │ Native GUI │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
4.4 分发模型
该架构支持一种简洁的分发方案,无需最终用户安装 Rust 工具链:
| 用户想要 | 他们下载的内容 | zeroclaw onboard 的作用 |
|---|---|---|
| 仅 CLI | zeroclaw 运行时二进制文件 | 配置提供程序,完成 |
| CLI + Discord | zeroclaw 运行时二进制文件 | 下载并安装 channel-discord.wasm |
| 本地 Web UI | zeroclaw + zeroclaw-gw | 配置两者,打开浏览器 |
| 桌面应用 | zeroclaw-desktop 安装程序 | 捆绑运行时 + 网关 + UI |
| 一切 | zeroclaw-desktop 或 zeroclaw --profile full | 下载所有插件 |
zeroclaw plugin install 命令(由已存在的 PluginHost 提供支持)将成为包管理器。zeroclaw onboard 向导会将其集成,使非技术用户无需接触 cargo。
4.4.1 版本控制策略
随着 ZeroClaw 从单个 crate 过渡到多 crate 工作区,从一开始就必须将两个问题分开处理:
- 产品版本:即
zeroclaw --version所报告的版本,也是 GitHub Releases、变更日志以及包管理器(Homebrew、apt、cargo-binstall)所跟踪的版本。这是运维人员和用户所认知的版本。 - 组件稳定性:指某个组件的成熟度和可靠性。单凭一个版本号无法传达这一信息。
这两者是正交的。将它们混淆会产生误导性的语义化版本(semver)噪声,并削弱对版本号的信任。本策略对两者都进行了定义。
Crate 版本管理:统一管理,并保留有意为之的例外
所有应用 crate、内核、网关、工具插件 crate、通道插件 crate 以及 CLI 都使用 Cargo workspace 包继承:根目录 Cargo.toml 中的单一版本即为权威的产品版本。采用此模型的原因如下:
- 用户、操作员和打包者只需处理一个版本,而不是十二个
- 通过
release-plz实现发布自动化非常简单:一个 PR,一次版本升级,一个变更日志条目 - 它体现了 ZeroClaw 作为产品的身份,而非库生态系统
- WIT 接口版本(而非 Rust crate 版本)才是实际的插件 ABI 契约(参见 §5.2)
有两类 crate 有意不采用工作区继承,而是按照各自的节奏维护独立版本:
| 箱 | 独立的原因 |
|---|---|
zeroclaw-api | 从 0.1.0 开始;其 1.0.0 版本是 v1.0.0 的正式里程碑交付物,标志着为插件 SDK 作者提供了稳定的 Rust 特性接口。 |
WIT 接口文件(wit/*.wit) | 通过 @since 和 @unstable 注解进行版本控制,遵循 WASI 组件模型规范;这些是主要的插件 ABI 契约,完全独立于 Cargo 的语义化版本控制(semver)。 |
产品版本中“breaking”(破坏性变更)的含义
由于应用程序 crate 共享统一的版本号,团队需要为破坏性变更制定一个产品级的定义,以区别于单个 crate 内部实现中的破坏性变更。如果插件 crate 内的破坏性变更未跨越下列任何边界,则不属于产品级破坏性变更,也不需要进行 MAJOR 版本升级。
| 更新 | 保证有效时 |
|---|---|
| 主要 | WIT 接口发生不兼容变更(现有插件必须重新编译);内核 IPC API 发生不兼容变更(网关或外部客户端会中断);配置文件架构需要迁移;CLI 命令或标志被移除或重命名 |
| 次要 | 工作区中的新功能;注册表中可用的新插件;稳定的新 API;稳定性级别提升;弃用公告(非移除) |
| 补丁 | 错误修复;安全补丁;文档修正;无新功能,无弃用 |
稳定性层级
产品版本回答“这是哪个版本?“,稳定性层级回答”我能在多大程度上依赖此组件?“每个组件、内核、网关、插件 crate 以及 WIT 接口都归属于三个层级之一。组件本地的 AGENTS.md 文件和插件注册表清单是目标所有权模型。在该迁移完成之前,当前规范性分配记录在 Coding agent guidelines 中。
| 层级 | 含义 | 含义 |
|---|---|---|
| 稳定 | 受产品变更策略的约束。在没有主版本升级和发布迁移指南的情况下,不会进行破坏性变更。 | 内核(目标:v0.8.0),zeroclaw-api WIT 接口(目标:v0.9.0),内核 IPC API(目标:v1.0.0) |
| 测试版 | 功能完整且经过测试。次要版本中允许包含破坏性变更,但会在更新日志中通过升级说明进行公告。 | zeroclaw-gw(v0.9.0 → v1.0.0),成熟的通道和工具插件 |
| 实验性 | 不提供稳定性保证。可能在 PATCH 版本中发生破坏性变更。必须在文档和插件注册表清单中明确标记为 experimental。 | 新的工具集成、新的通道实现、早期硬件插件 |
稳定性层级通过团队有意的决策进行提升,绝不会降级。提升记录在变更日志中,对于架构组件,还需记录在架构决策记录(ADR)中。一个组件必须在其当前层级至少保持一个完整的发布周期,才能考虑提升。
发布自动化
发布使用 release-plz,它会在推送到 master 时创建发布 PR、提升工作区版本,并根据 Conventional Commits 提交标题生成更新日志。release-plz 原生支持工作区继承,并自动处理 crate 的发布顺序。独立进行版本管理的 zeroclaw-api crate 使用同一工具的按 crate 配置单独管理。
4.4.2 发布制品
微内核转换改变了“哪些功能被编译进去”这一问题的根本性质。目前,该问题只有一个答案:你传递给 cargo build 的任意功能标志。转换之后,它被拆分为两个独立的问题:
- 内核二进制文件中包含的内容:在编译时固定,按平台确定,发布到 GitHub Releases
- 有哪些可用功能:在运行时根据通过
zeroclaw plugin install安装了哪些插件来确定
这些问题已不再相同,当前 Cargo.toml 中的 [features] 部分必须从这个角度来解读。
当前编译期功能标志的去向
随着架构的成熟,当前 Cargo.toml 中的 20 多个功能标志可分为三类:
| 存储桶 | 标志 | 结果 |
|---|---|---|
| 退休 → 插件 | channel-nostr、channel-matrix、channel-lark、whatsapp-web、browser-native | 已从内核中移除。每个模块都作为 WASM 插件 crate 发布到插件注册表中。无需在编译时做出决策。 |
| 始终开启 | plugins-wasm,skill-creation | 无条件编译到每个内核二进制文件中。plugins-wasm 是内核的核心机制;skill-creation 是一个零开销的代码路径。两者都不应依赖于任何标志。 |
| Stay → 平台/基础设施标志 | peripheral-rpi, hardware, sandbox-landlock, sandbox-bubblewrap, voice-wake, probe | 保留为编译时标志,因为它们需要原生库链接或操作系统级别的访问权限,而这些功能无法由 WASM 插件提供。peripheral-rpi 和 hardware 仅出现在特定平台的发布目标中。 |
plugins-wasm 始终启用,但它不是一个单独的标志:它是一个三标志分类。宿主机制是无条件的;执行后端则是一个在构建时做出的平台级决定。没有后端子标志的 plugins-wasm 不会生成可用的插件运行时,因为 wasmtime 需要编译器或解释器之一来执行组件。
| 标志 | 默认 | 目的 |
|---|---|---|
plugins-wasm | 始终开启 | 启用 WASM 组件主机;加载并执行 .wasm 组件文件 |
plugins-wasm-cranelift | 在(支持的情况下) | Cranelift JIT 编译;用于 x86_64、aarch64 以及其他 Cranelift 支持的目标 |
plugins-wasm-pulley | 在(Cranelift 不可用时) | Pulley 解释器;用于 32 位 ARM 以及任何其他无法使用 Cranelift 的目标平台 |
每个发布目标恰好启用一个后端:在受支持的地方使用 cranelift,在不受支持的地方使用 pulley。始终启用的意图保持不变:每个二进制都包含插件宿主,并且可以在其平台上执行插件。
有两个标志需要在 v0.8.0 版本发布前由团队做出明确决策,因此在此处提出,而非单方面解决:
observability-prometheus:目前位于default中。Prometheus 指标会带来可观测的二进制体积开销。问题在于生产运行时是否应默认启用可观测性,还是由运维人员主动选择启用。建议:在标准发行版中保留于default中;针对体积受到严格限制的目标,运维人员可以使用--no-default-features进行构建。observability-otel:OTLP 导出会带来更大的依赖体积(opentelemetry + reqwest 阻塞客户端)。建议:保持可选启用,不纳入default。需要导出追踪数据的生产部署应显式启用它。
随着通道和工具标志的弃用,ci-all 元特性大幅简化。到 v1.0.0 版本时,它仅涵盖剩余的平台和基础设施标志。
规范的发布内核二进制文件
为每个平台目标发布到 GitHub Releases 的二进制文件是使用以下配置构建的:
| 编译在 | 未编译 |
|---|---|
| 核心代理循环 | 任何通道实现 |
| 10–12 个核心工具(参见第二阶段 D2) | 任何非核心工具 |
| SQLite + Markdown 内存后端 | 浏览器自动化 |
插件主机(plugins-wasm,始终开启) | observability-otel(操作员选择加入) |
observability-prometheus | voice-wake(依赖 libasound2) |
skill-creation(零开销) | probe(小众硬件调试) |
| IPC 服务器 | Web 资源(已移至 zeroclaw-gw) |
| 支持的平台沙箱 | peripheral-rpi(独立的硬件构建) |
不再提供“构建所有组件”的二进制文件。该概念模型已被 zeroclaw plugin install --profile full 取代,该命令会在安装精简内核二进制文件后下载完整的插件目录。
发布制品矩阵
每个 GitHub 版本会发布以下制品:
| 工件 | 目标 | 备注 |
|---|---|---|
zeroclaw 内核二进制文件 | x86_64-unknown-linux-musl, aarch64-unknown-linux-gnu, armv7-unknown-linux-gnueabihf, x86_64-apple-darwin, aarch64-apple-darwin, x86_64-pc-windows-msvc | 适用于 Linux x86_64 的静态 musl 构建;适用于 ARM 目标的 GNU |
zeroclaw 内核二进制文件(硬件) | aarch64-unknown-linux-gnu, armv7-unknown-linux-gnueabihf | 相同的构建目标,使用 peripheral-rpi 和 hardware 标志编译,适用于树莓派部署 |
zeroclaw-gw 网关二进制文件 | 与内核相同的平台矩阵 | 与内核一同发布;用户需单独安装 |
| WASM 插件文件 | wasm32-wasip2 | 已发布到插件注册表(非 GitHub Releases);可通过 zeroclaw plugin install 安装 |
zeroclaw-desktop 安装程序 | 适用于 macOS、Windows、Linux(AppImage/deb)的 x86_64 和 aarch64 | 捆绑内核 + 网关 + 完整插件集;由 Tauri 工作流构建 |
wasm32-wasip2 插件构建在独立的 CI 作业中运行,并按其自身的节奏发布到插件注册表。插件发布无需进行内核发布。
4.5 网关分离
当前的网关混淆了两个必须分离的概念:
Current (wrong):
zeroclaw binary
└── gateway
├── Web UI server (serves React app)
├── REST/WS/SSE API
├── WhatsApp webhook handler ← this is a channel, not a web server
├── Linq webhook handler ← this is a channel, not a web server
├── Nextcloud webhook handler ← this is a channel, not a web server
└── Gmail push handler ← this is a channel, not a web server
Target (correct):
zeroclaw-kernel
└── Local IPC API (Unix socket / 127.x HTTP)
zeroclaw-gw (separate binary, optional)
└── Connects to kernel IPC API
└── Web UI server
└── REST/WS/SSE API
└── Generic webhook proxy → routes to channel plugins
channel-whatsapp.wasm
└── Registers its own webhook route with the gateway
└── Handles WhatsApp-specific message parsing
为何重要: 当网关是一个独立进程时,它可以崩溃、重启或缺失而不会影响 agent。kernel 会持续运行。这对边缘硬件使用场景尤为重要:一台运行 kernel 的 Raspberry Pi 可以将其 Web UI 由 VPS 提供,而 kernel 通过 channel 插件向外连接。无需任何入站防火墙规则。
5. 我们应该采用的标准
标准是许多聪明人在多年间达成的共识。采用这些标准意味着我们可以免费获得这些年的思考成果,并且我们的软件能够与生态系统自然集成。以下是直接适用于 ZeroClaw 的标准。
5.1 可观测性:OpenTelemetry
它是什么: OpenTelemetry(OTel)是用于从软件系统收集追踪、指标和日志的行业标准。它由云原生计算基金会(Cloud Native Computing Foundation)维护,并得到所有主要云提供商和监控工具的支持。
为何对 ZeroClaw 至关重要: 我们已经基于 Observer trait 实现了 OtelObserver。我们拥有 Prometheus 指标和 DORA 指标。问题在于,这些尚未在整个代码库中实现标准化:有些模块使用 tracing::info! 记录日志,另一些则发出 ObserverEvent,而这两者之间并未建立关联。
我们应该做什么:
- 采用 OpenTelemetry 作为所有组件的统一可观测性接口
- 确保每个插件在执行时都生成 OTel 跨度,以便用户可以看到从“在 Discord 上收到消息”到“代理调用 shell 工具”再到“发送响应”的完整追踪。
- 采用 W3C Trace Context(
traceparent/tracestate头)在 kernel ↔ gateway ↔ plugin 边界上传播 trace ID - 当设置
ZEROCLAW_LOG_FORMAT=json时,结构化日志输出应为 JSON 格式(已使用tracingcrate,只需添加一个 JSON 订阅器即可)。
标准: OpenTelemetry 规范 · W3C Trace Context (REC) · RFC 5424 (Syslog,用于系统日志集成)
5.2 插件接口:WASI 和 WIT
它是什么: WASI(WebAssembly System Interface,WebAssembly 系统接口)是 WebAssembly 模块用于与宿主系统交互的标准 API。WIT(WebAssembly Interface Types,WebAssembly 接口类型)是用于描述 WASM 组件导出和导入内容的接口定义语言:可以把它看作是面向 WASM 插件的 .proto 文件。
对 ZeroClaw 的重要性: 我们的 WasmTool 和 WasmChannel 桥接器目前缺乏对插件 WASM 二进制文件必须导出的内容的正式约定。这意味着插件作者必须猜测。WIT 文件精确定义了该约定,并支持为任何语言的插件作者自动生成代码。
我们应该做什么:
- 为
Tool、Channel和Memory插件类型定义 WIT 接口文件(在工作区根目录下的wit/目录中) - 使用
wit-bindgen从这些 WIT 文件生成 Rust 主机端绑定 - 将 WIT 接口记录为官方插件 SDK
- 插件作者使用 Rust(或 Go、C、Python)针对 WIT 接口编写代码,然后执行
cargo build --target wasm32-wasip2:生成的结果会被放入~/.zeroclaw/plugins/
标准: WASI 0.2 · W3C WebAssembly 组件模型 · WIT IDL
5.3 本地 API:OpenAPI 3.1
它是什么: OpenAPI 是描述 HTTP API 的标准。3.1 版本与 JSON Schema Draft 2020-12 保持一致。
对 ZeroClaw 的重要性: 内核的本地 IPC API(网关和其他组件连接的套接字)需要一个稳定、有文档支持的契约。如果没有正式规范,网关和内核会随时间推移而无声地偏离。
我们应该做什么:
- 在实现之前,为内核的本地 IPC API 编写 OpenAPI 3.1 规范
- 使用
utoipa或aide从规范生成 Rust 服务端存根 - 将规范发布为
docs/reference/api/kernel-ipc-api.yaml - 网关的外部 API 也应包含 OpenAPI 规范
标准: OpenAPI 3.1 · JSON Schema Draft 2020-12
5.4 安全性:OWASP ASVS
它是什么: OWASP 应用安全验证标准(OWASP Application Security Verification Standard)是一份按风险等级(L1 基础、L2 标准、L3 高级)组织的安全需求清单。
为何对 ZeroClaw 至关重要: 该网关处理来自外部服务的 webhook、处理不可信的用户输入并管理密钥。配对系统、WebAuthn 支持和速率限制都已存在,但目前没有用于验证它们是否完整或正确的框架。
我们应该做什么:
- 目标 ASVS 第 2 级适用于网关和安全模块
- 完成 Level 2 检查清单,并记录我们满足哪些要求、部分满足哪些要求,以及哪些要求不在范围内。
- 将其作为与安全相关的问题和 PR 的基础
标准: OWASP ASVS 4.0 · OWASP Top 10
5.5 质量模型:ISO/IEC 25010
它是什么: ISO/IEC 25010 定义了一个软件产品质量模型,包含八个顶层特性:功能适用性、性能效率、兼容性、可用性、可靠性、安全性、可维护性和可移植性。
对 ZeroClaw 的意义: 当有人问“这是否足够好以合并?”时,答案目前具有主观性。ISO 25010 为我们提供了用于讨论的术语体系。愿景承诺直接对应如下:“零开销” → 性能效率;“任意硬件” → 可移植性;“零妥协” → 安全性 + 可靠性。
我们应该做什么:
- 在 PR 审查中,使用这八个质量特性作为审视重大变更的视角
- 在针对架构变更的 PR 模板中包含简短的质量影响说明(例如:“此变更通过降低网关与通道实现之间的耦合性来提升可维护性,且不影响性能效率”)
标准: ISO/IEC 25010:2023
5.6 已采用:保留这些
这些已经就位,应保持其状态:
| 标准 | 状态 | 在哪里 |
|---|---|---|
| 语义化版本控制 2.0.0 | ✅ 已采纳 | Cargo.toml,发行版 |
| 常规提交 | ✅ 已采纳 | AGENTS.md,提交历史 |
| RFC 3339 / ISO 8601 时间戳 | ✅ 已采纳 | MemoryEntry,所有时间戳 |
| XDG 基础目录规范 | ✅ 已采纳 | 正在使用的 directories 库 |
| 保持变更日志 | ✅ 已采纳 | CHANGELOG.md |
| Rust API 指南 | ✅ 部分 | Clippy 配置强制执行许多 |
6. 分阶段路线图:v0.7.0 → v1.0.0
每个阶段都遵循“愿景 → 架构 → 设计 → 实现 → 测试 → 文档 → 发布”的层级结构。在某个阶段的设计被审查并达成一致之前,不会开始该阶段的实现工作。
整体迁移策略采用绞杀榕模式:我们在新架构的边缘逐步构建,并持续向内迁移,直到旧结构被完全替换。我们从不进行“停止世界”式的重写。应用程序始终处于可交付状态。
阶段 1 · v0.7.0:“接缝”
主题: 在不改变任何行为的前提下,使架构可见。先绘制线条。
为何需要此阶段: 在各层成为真正的边界之前,你无法迁移到分层架构。目前,这些 trait 定义了逻辑接缝,但编译器并不强制执行它们:所有内容都在一个 crate 中,因此任何内容都可以导入任何内容。此阶段使这些接缝成为现实。
愿景对齐: 所有视觉属性对用户均无变化。这完全是内部机制。其价值在于,每个未来的贡献现在都有了结构化的归属,新贡献者可以分部分地理解代码库,而不必一次性全部掌握。
阶段 1 交付物
D1:提取 zeroclaw-api crate
创建一个新的 crate crates/zeroclaw-api,其中仅包含 trait 定义及其支持类型。不包含任何实现。不依赖重型依赖项。该 crate 的编译时间应控制在两秒以内。
进入此 crate:
src/providers/traits.rs→Provider、ChatMessage、ChatResponse、ToolCall、StreamChunk、ProviderCapabilitiessrc/channels/traits.rs→Channel、ChannelMessage、SendMessagesrc/tools/traits.rs→Tool、ToolResult、ToolSpecsrc/memory/traits.rs→Memory、MemoryEntry、MemoryCategorysrc/observability/traits.rs→Observer、ObserverEvent、ObserverMetricsrc/runtime/traits.rs→RuntimeAdaptersrc/peripherals/traits.rs→Peripheral
工作区中的其他所有需要这些类型的 crate 都将 zeroclaw-api 添加为依赖项。编译器现在强制执行以下规则:任何实现 crate 都不能绕过 API 层直接导入另一个实现 crate。
D2:提取 zeroclaw-tool-call-parser crate
src/agent/loop_.rs 中的工具调用解析逻辑大约包含 1,400 行纯文本转换代码:它接收来自 LLM 的字符串,并返回结构化工具调用列表。该模块不依赖于代理状态、内存、提供商或通道。它支持十几种不同的 LLM 输出格式(JSON、XML、GLM 风格、MiniMax、Perl 风格、markdown 代码块等)。
这个逻辑是:
- 自包含:非常适合作为独立的 crate
- 本项目中最适合进行模糊测试的代码:基于属性的测试应放在此处
- 对 Rust 生态系统的真正贡献:没有其他 crate 能如此全面地实现这一点
创建 crates/zeroclaw-tool-call-parser,其公共 API 大致如下:
#![allow(unused)]
fn main() {
pub fn parse(text: &str, specs: &[ToolSpec]) -> ParseResult
pub struct ParseResult {
pub calls: Vec<ParsedToolCall>,
pub remaining_text: Option<String>,
}
pub struct ParsedToolCall {
pub name: String,
pub arguments: serde_json::Value,
pub tool_call_id: Option<String>,
}
}
loop_.rs 中现有的约 300 个解析测试将移至本 crate。loop_.rs 将减少约 1,400 行代码。
D3:采用 OpenTelemetry 作为可观测性标准
规范化现有实现:文档说明 ObserverEvent 和 ObserverMetric 是内部事件总线,而 OtelObserver 是标准的后端实现。为 ZEROCLAW_LOG_FORMAT=json 添加 JSON 结构化日志记录订阅者。采用 W3C Trace Context 以支持未来的跨组件追踪。
D4:编写 WIT 接口文件
在实现 WASM 插件执行之前,先定义接口契约。在工作区根目录创建一个 wit/ 目录,用于存放以下接口定义:
zeroclaw:tool/tool.wit:Tool 插件接口zeroclaw:channel/channel.wit:Channel 插件接口
这些将成为官方的插件 SDK。v0.8.0 中的实现将从这些文件生成。
v0.7.0 的成功指标
zeroclaw-api在不到 2 秒内编译完成,且无任何实现依赖zeroclaw-tool-call-parser的测试覆盖率 ≥ 95%(该逻辑完全可独立测试)loop_.rs文件行数不到 8,000 行- 零用户可见的行为变更
- 零性能回退(基准测试套件通过)
阶段 2 · v0.8.0:“运行时”
主题: 将代理运行时形式化为一个干净、可独立部署的单元。所有非运行时部分均视为访客。
此阶段的意义: 一旦接缝就位(v0.7.0),我们便能明确划定运行时边界。本阶段将 zeroclaw-runtime 提取为独立的 crate,完善 WASM 插件执行桥接,并接入插件注册表客户端:即运行时之外的一切与之连接的机制。
愿景对齐: 这正是组合模型对用户变得真实可感的地方。一个只需要 CLI agent 的用户下载一个二进制文件,运行 zeroclaw onboard,即可完成:无需 Rust 工具链,无需编译。zeroclaw onboard 向导获得了按需下载插件组件的能力。
第二阶段交付物
D1:正式确立 zeroclaw-runtime crate
将代理编排循环、CLI 通道、安全策略、插件主机和 IPC API 提取到 crates/zeroclaw-runtime 中,并通过 agent-runtime 特性进行条件编译。该 crate 依赖于 zeroclaw-api 和基础 crates。它不依赖 Telegram、Discord、Anthropic 或任何特定工具的实现。
运行时导出了一个干净的公共 API:
#![allow(unused)]
fn main() {
pub struct Runtime { ... }
pub struct Registry {
pub fn register_channel(&mut self, ch: Arc<dyn Channel>);
pub fn register_tool(&mut self, t: Box<dyn Tool>);
pub fn set_provider(&mut self, p: Arc<dyn Provider>);
pub fn set_memory(&mut self, m: Arc<dyn Memory>);
pub fn set_observer(&mut self, o: Arc<dyn Observer>);
}
pub async fn run(runtime: Runtime, registry: Registry) -> anyhow::Result<()>;
}
二进制 crate 变成了一个薄层的接线层,它读取配置并调用 run。
D2:完成 WASM 执行桥接
extism 依赖项与 WASM 组件模型(.wit 文件)不兼容,并且需要 wasmtime 的 cranelift 特性,这会阻止 ARM32 目标平台的编译。请移除 Extism,并将其替换为直接使用 wasmtime。在过渡期间,应将 Extism 保留为一个可选项,直到最终的弃用 PR 提交为止。
将 wasmtime 接入 zeroclaw-plugins,并对 cranelift(用于大多数构建目标)或 pulley(用于 ARM32)设置可选依赖。在 v0.7.0 中已定义 WIT 接口的情况下,使用 wit-bindgen 生成主机端绑定。
完整的 WASM 执行桥接实现定义了 WASM 插件可以在 PluginPermission 中已定义的权限模型内调用的 WASI 宿主函数(HTTP 请求、内存访问、日志记录)。在可能的情况下,应使用 WASI Preview 2 API(wasi:io、wasi:http、wasi:filesystem 等),为插件提供一致的、基于标准的 API。
D3:组件注册表客户端
添加一个由简单注册表客户端支持的 zeroclaw plugin 子命令:
zeroclaw plugin list # list installed plugins
zeroclaw plugin search <query> # search the component registry
zeroclaw plugin install <name> # download, verify, and install a plugin
zeroclaw plugin remove <name> # remove an installed plugin
zeroclaw plugin update # update all installed plugins
注册表是一个由已知 URL 提供服务的 JSON 索引文件(例如 https://plugins.zeroclaw.com/index.json)。每个条目包含名称、版本、下载 URL、SHA-256 校验和以及发布者的 Ed25519 公钥。PluginHost 的签名验证已经处理了安全模型。
D4:将 zeroclaw onboard 与插件系统集成
引导向导应询问用户希望选择哪些频道和集成,然后为每个选项调用 PluginRegistry::install。无需编译。用户只需下载二进制文件,运行 zeroclaw onboard,即可在不到两分钟内拥有一个已配置好的工作代理。
D5:将 all_tools_with_runtime 精简为仅核心工具
内核包含了用户在使用无插件安装的有用代理时所需的全部工具:shell、file_read、file_write、file_edit、git_operations、glob_search、content_search、memory_recall、memory_store、memory_forget 和 web_fetch。其余功能均由已安装的插件注册。
v0.8.0 的成功指标
zeroclaw-runtime独立编译,不包含任何通道或工具实现代码zeroclaw plugin install channel-discord端到端工作正常zeroclaw onboard安装插件,无需 Rust 工具链- 运行时二进制文件的大小在发布说明中被跟踪和报告;目标是朝着愿景目标(见第7节)实现持续下降。
- 一个使用 WIT 接口编写的 Rust WASM 工具插件执行正确
第三阶段 · v0.9.0:“网关”
主题: 将 Web 界面与代理核心分离。
为什么需要这个阶段: 网关目前是代码库中最大的结构性耦合点。它嵌入了一个编译后的 React 应用,处理特定通道的 webhook 逻辑,并被编译进每一个二进制文件中,包括那些面向 10 美元边缘硬件、永远不会提供网页服务的二进制文件。
愿景对齐: 此阶段完全兑现了“零外部依赖”的承诺。在树莓派上,用户将获得一个内核二进制文件,其中不包含 Web 服务器、React 应用或 HTTP 监听器。希望使用 Web 仪表板的用户需单独安装 zeroclaw-gw。
第 3 阶段交付物
D1:定义内核 IPC API
在提取网关之前,先为内核在 Unix 套接字或回环端口上暴露的本地 API 定义 OpenAPI 3.1 规范。该 API 是网关、Tauri 应用以及任何未来客户端所连接的接口,也是内核与外部世界之间的稳定契约。
端点包括:发送消息、接收流式响应、列出活动会话、列出已安装的插件、获取代理状态、管理内存、触发 cron 作业。这首先是一份设计文档:应在编写任何一行实现代码之前对规范进行审查并达成一致。
D2:实现内核 IPC 服务器
在 zeroclaw-kernel 中通过特性标志(--features ipc)添加 IPC 服务器。在支持的平台(如 Unix 系统)上,内核会在 ~/.zeroclaw/kernel.sock 处的 Unix 套接字上监听。在 Windows 上,使用命名管道。zeroclaw gateway 命令(当前 Web 服务器的入口点)将变为 zeroclaw-gw,并连接到该套接字。
D3:将 zeroclaw-gw 提取为独立的二进制文件
将 src/gateway/ 移动到一个新的 crates/zeroclaw-gw/ crate 中,并配备其自己的二进制文件。它依赖 zeroclaw-api,并通过 IPC API 连接到内核。通过 rust-embed 嵌入的 React 应用程序将完全迁移到此 crate 中:内核二进制文件不再包含任何 web 资源。
D4:将通道 webhook 处理程序从网关中迁移出来
目前位于 gateway/mod.rs 中的 WhatsApp、Linq、Nextcloud Talk 和 Gmail webhook 处理程序移至各自的渠道插件中。网关提供通用的 webhook 注册 API:渠道插件加载后,会注册其 webhook 路径前缀和处理函数。网关将传入的 webhook 路由到已注册的处理程序。网关不再了解 WhatsApp。
D5:正式确立 Tauri sidecar 关系
更新 apps/tauri/,将 zeroclaw-gw 作为 Tauri sidecar 二进制文件进行打包。Tauri 应用成为“完整体验”分发版本:它会自动启动内核和网关,并打开 Web UI。下载 Tauri 应用的用户无需操作终端即可让一切正常运行。
v0.9.0 的成功指标
- 内核二进制文件(发布版)不包含任何 Web 资源或 HTTP 服务器代码
zeroclaw-gw启动,通过 IPC 连接到内核,并提供 Web 仪表板- 移除
zeroclaw-gw不会破坏内核或任何通道插件 - WhatsApp、Linq、Nextcloud Talk 和 Gmail 的渠道代码已移至插件 crate。
- Tauri 桌面应用包正确打包并启动了两个二进制文件
阶段 4 · v1.0.0:“平台”
主题: ZeroClaw 成为一个可组合的平台,而非单体应用。
为何选择此阶段: 随着内核稳定、网关分离以及插件系统正常运行,v1.0.0 将成为架构转化为产品的关键版本。外部开发者可以编写并发布插件,用户能够按需组装他们想要的 ZeroClaw。该二进制文件也能切实兑现其轻量级架构的承诺。
第 4 阶段交付物
D1:将所有剩余的通道迁移到插件
每个 27+ 通道实现都成为一个独立的 WASM 插件 crate。它们以签名发布的形式发布到组件注册表中。内核二进制文件不包含任何通道实现,除了 CLI。
D2:将长尾工具迁移至插件
大约 60 个工具被移至插件 crate,并按领域分组:zeroclaw-tools-web(浏览器、搜索、截图、PDF)、zeroclaw-tools-integrations(Jira、Notion、Google Workspace、MS365、LinkedIn)、zeroclaw-tools-hardware(板级信息、GPIO)、zeroclaw-tools-cloud(云运维、安全运维)。内核仅保留 v0.8.0 中确定的 10–12 个核心工具。
D3:插件 SDK 与开发者文档
发布插件开发指南。开发者应能在一个下午内编写一个新的工具插件:
- 将
zeroclaw-plugin-sdk添加为依赖项 - 实现由 WIT 生成的 trait
cargo build --target wasm32-wasip2zeroclaw plugin install ./my-plugin/
SDK 负责处理主机函数绑定、清单格式以及权限模型。
D4:将内核 IPC API 稳定到 v1.0
内核 IPC API 具有版本前缀(/v1/)和稳定性保证。该 API 不允许在 v1.x 中进行破坏性更改。这是第三方客户端和网关所依赖的契约。
D5:将版本控制策略和稳定性层级定义提取到 docs/book/src/maintainers/stability-tiers.md
本 RFC 第 4.4.1 节中定义的版本控制策略和稳定性层级表将成为常驻的贡献者参考文档,位于 docs/book/src/maintainers/stability-tiers.md。该文档是贡献者在为新插件 crate 分配层级时日常使用的参考,也是维护者在做出发布决策时查阅的资料。本 RFC 本身保留了这些决策背后的原因(why);而提取出的文档则是贡献者查阅的具体内容(what)。
v1.0.0 的成功指标
- 运行时二进制大小会对照愿景目标进行跟踪(见§7);作为 v1.0.0 工作流的一部分,预计将对每个 crate 执行专门的优化流程。
- 第三方开发者仅使用公开文档即可发布一个可用的插件
- 所有 27 多个通道实现均可作为可下载插件在注册表中获取
zeroclaw onboard在未安装 Rust 工具链的 Raspberry Pi Zero 2W 上,可在不到 2 分钟内完成完整设置。- 完整的插件目录可通过
zeroclaw plugin install --profile full安装
7. 代码与复杂度指标
这些估计值是基于对当前代码库的直接代码分析得出的。它们旨在提供规模感,而非精确预测。
移出运行时的代码行数
| 什么在移动 | 近似行 | 目的地 |
|---|---|---|
工具调用解析器(来自 loop_.rs) | ~1,400 | zeroclaw-tool-call-parser 库 |
| 60 多个非核心工具实现 | 约 30,000 | 插件 crate |
| 24+ 非核心通道实现 | ~7,200 | 插件 crate |
| 网关 HTTP 服务器 | ~2,260 | zeroclaw-gw 库 |
| 嵌入式 React 应用(二进制权重) | N/A | zeroclaw-gw 库 |
| 从网关转发通道 webhook 处理程序 | ~500 | 通道插件 crate |
| 估计从运行时中移除的总量 | 约 41,000 行 | N/A |
文件级复杂度降低
| 文件 | 当前行 | 迁移后的目标 | 归约 |
|---|---|---|---|
src/agent/loop_.rs | 约 9,500 | ~5,000 | ~47% |
src/gateway/mod.rs | ~2,260 | 移动到 zeroclaw-gw | 100% |
src/tools/mod.rs | all_tools_with_runtime 大约有 680 行 | 约 80 行(仅核心工具) | ~88% |
src/providers/mod.rs | ~3,750 | 约 1,200(提供商自行注册) | ~68% |
src/channels/mod.rs | ~200 + 44 通道文件 | 仅限 CLI 通道 | ~90% |
二进制文件大小:实测进展与目标愿景
项目的愿景以运行时指标表达:在 $10 硬件上实现 <5 MB RAM。磁盘上的二进制大小与运行时内存占用(RSS)相关但并不完全相同:按需分页意味着只有被执行的代码路径才会驻留内存。两者都会被跟踪。
两阶段模型: 架构分解(阶段 1–3)与二进制体积优化是独立的工作流。分解通过将依赖项隔离到其所属的 crate 中,从而为优化创造条件。按 crate 最大化效率是预期的第二阶段工作,而非结构本身工作的交付成果。
| 配置 | 预分解(v0.6.x) | 第一阶段结果(v0.7.0) | 视觉目标 |
|---|---|---|---|
| 完整的单体二进制文件 | 约 8.8 MB | 不适用(由插件模型替换) | N/A |
仅基础功能(--no-default-features) | N/A | 6.6 MB (测量值,已剥离) | 优化传递后待定 |
运行时二进制文件(基础 + agent-runtime) | N/A | 已跟踪 | 内存占用:运行时 ≤ 5 MB |
| 运行时 + 网关 | N/A | 已跟踪 | ~5–7 MB 磁盘空间 |
| 运行时 + 网关 + 前 5 个频道 | N/A | 已跟踪 | 约 8–10 MB(插件为独立文件) |
| Tauri 桌面应用(包含所有依赖) | N/A | 已跟踪 | ~20–25 MB 安装程序 |
6.6 MB 的第 1 阶段基础构建相较于 8.8 MB 的单体应用代表了实质性的进展,并证明了解耦正在生效。要达到愿景目标,需要在结构性解耦完成后,对每个 crate 进行专门的依赖审计和优化处理:审查每个 crate 的 Cargo.toml 以排查不必要或功能过载的依赖项,验证 LTO 和 strip 配置,并审计实际需要哪些 tokio/serde 功能标志。
关键的结构转变:二进制大小不再取决于“在构建时编译的功能”,而是取决于“在运行时安装的插件”,由用户控制。这一转变是第 1–3 阶段的架构目标。而大小数字则是后续阶段的优化目标。
编译时间改进
目前,对该代码库执行完整的 cargo build --release 会在单个编译单元中编译所有通道、所有工具、所有提供程序以及嵌入式 React 应用。Crate 分解意味着:
- 内核独立编译,其编译输出会被缓存
- 对
channel-discord的更改不会重新编译内核 - 仅重新编译其插件
- CI 可以在作业间并行化 crate 编译
增量构建的估计墙钟时间改进:对于未触及内核的更改,可减少 60–75% 的时间。
8. 这对贡献者意味着什么
对于新贡献者
大型代码库的新贡献者最常见的抱怨是:“我不知道从哪里开始。” 在当前架构下,要回答“一条 Discord 消息会流向哪里?”这个问题,需要追踪 channels/discord.rs → channels/mod.rs → gateway/mod.rs → agent/loop_.rs → 数十个其他文件。
采用微内核架构时,答案是:“消息会发送到内核的 Channel 接收端,通过 channel-discord 插件。” 新贡献者只需阅读一个插件 crate 即可完全理解 Discord 频道。他们只需阅读 zeroclaw-kernel,无需涉及任何频道或工具代码,即可理解完整的代理循环。
新贡献者的一条实用经验法则: 如果你能用一句话描述你的改动,且不涉及超过一个组件,那么你的工作粒度就是合适的。“修复 Discord 频道处理线程回复的一个 bug”是一个组件。“重构 agent 循环、更新 Discord 频道,同时修复 memory 后端”则涉及三个组件:它应当拆分为三个 PR。
对于维护者
每个错误报告都会有一个明确的归属。例如,“代理调用工具不正确” → zeroclaw-tool-call-parser 或 zeroclaw-runtime。“Discord 集成出现问题” → channel-discord 插件。“Web 仪表板无法加载” → zeroclaw-gw。目前,这些错误可能出现在 50,000 多行代码中的任何位置。
对于发布流程
插件模型意味着频道和工具可以拥有独立的发布周期。Telegram 频道的错误修复不需要新的内核发布。内核的稳定性成为其他一切构建的基础。对插件的快速迭代不会危及内核的稳定性。
面向社区
已发布的 WIT 接口和插件 SDK 意味着任何人都可以在不 Fork 的情况下扩展 ZeroClaw。需要特定集成的公司可以针对公开接口编写插件。这就是生态系统构建的方式。
附录 A:术语表
本文档中可能包含一些不熟悉的术语:
大泥球(Big Ball of Mud):一种代码库在缺乏结构规划的情况下野蛮生长而形成的架构(或者说毫无架构可言)。这一名称源自 Brian Foote 和 Joseph Yoder 在 1997 年发表的一篇论文。它是软件中最常见的架构,并非因为有人主动选择了它,而是因为它是你什么都不做时默认得到的结果。
康威定律:“任何设计系统的组织,所产生的设计结构都是该组织沟通结构的镜像。”(Mel Conway,1968)如果贡献者各自为政、互不沟通,代码就会反映出这一点。如果贡献者在各自工作之间以清晰的接口进行协作,代码同样会反映出这一点。
依赖倒置原则:高层模块不应依赖低层模块,两者都应依赖于抽象。这就是为什么 zeroclaw-runtime 依赖于 zeroclaw-api(抽象),而不依赖于 channel-discord(具体实现)。
微内核:一种架构,其中核心系统仅包含最低限度的必要功能,所有其他能力均由独立组件提供,这些组件通过定义良好的接口与核心进行通信。
绞杀者无花果模式(Strangler Fig Pattern):一种迁移策略,通过在旧组件旁边构建新组件,逐步替换现有系统的各个部分。该模式得名于绞杀榕这种植物,它会环绕现有树木生长,直至完全取代原本的树木。其核心特性在于:在整个迁移过程中,系统始终保持运行且始终可部署。
技术债务:在软件设计中走捷径所累积的代价。如同金融债务,少量负债可能带来收益(你现在能更快交付),而大量负债则会令人举步维艰(你把所有时间都耗在偿还利息上,即修复缺陷和应付变通方案,而非开发新功能)。
WIT (WebAssembly Interface Types):一种接口定义语言,用于描述 WASM 组件导出和导入的内容。可以将其视为一份契约:“Tool 插件必须导出一个名为 execute 的函数,该函数接受 JSON 并返回 JSON。” WIT 让这份契约变得精确且可被机器读取。
附录 B:延伸阅读
这些是团队可能会觉得有价值的资源。它们不是必读内容,但每一项都直接影响了本提案。
-
《软件设计的哲学》:John Ousterhout 著。关于管理软件复杂性的最佳简明读物。他提出的“深模块“概念(简单的接口、强大的实现)正是微内核模型所追求的目标。
-
《整洁架构》(Clean Architecture):Robert C. Martin 著。本文档第 4.2 节中描述的依赖规则源自本书。
-
《Release It!》:Michael Nygard 著。介绍构建可在生产环境中持续稳定运行的软件的实用模式。本文讨论的网关分离和断路器模式即源自本书。
-
The Rust API Guidelines:设计符合 Rust 习惯的库的官方指南。我们的 trait 接口应遵循这些约定。
-
WebAssembly 组件模型:本 RFC 所提议的插件系统的技术基础。
-
OpenTelemetry 规范:我们正在采用的可观测性标准的完整规范。
本提案基于对 ZeroClaw 代码库 v0.6.8 版本的详细分析制定。所引用的代码指标均源自对源文件的直接测量。架构建议反映了在系统软件设计中采用的成熟模式,并结合了 ZeroClaw 项目的具体约束与目标。
欢迎提供反馈、纠正意见和不同的提议。最好的架构是团队所理解并认同的架构,而不是由任何一个人独断决定的架构。