ZeroClaw 架构分析
一句话总结
ZeroClaw 是一个用 Rust 写的 AI 智能体运行时——你可以把它想象成一个"AI 机器人的操作系统",它能连接各种大模型(OpenAI、Claude、Gemini……)、各种聊天平台(Telegram、Discord、微信……)、各种工具(文件操作、网页浏览、Shell 命令……),甚至还能直接控制硬件(树莓派、STM32 开发板),而且整个二进制文件只有 8.8MB,运行内存不到 5MB。
整体架构鸟瞰
┌─────────────────────────────────────────────────────────────┐
│ 用户 / 消息平台 │
│ Telegram Discord Slack 微信 邮件 IRC CLI ... │
└──────────────────────────┬──────────────────────────────────┘
│ ChannelMessage
▼
┌─────────────────────────────────────────────────────────────┐
│ Gateway(网关层) │
│ HTTP/WebSocket/SSE 接入 · 配对认证 · 签名校验 · 幂等去重 │
└──────────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Agent 编排循环(大脑) │
│ │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 记忆召回 │──▶│ 提示构建 │──▶│ LLM 调用 │──▶│ 工具调度 │ │
│ └─────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ▲ │ │
│ └───────────── 工具结果反馈 ◀────────────────┘ │
└───┬───────────┬───────────┬───────────┬─────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌───────┐ ┌─────────┐ ┌─────────┐ ┌──────────┐
│Provider│ │ Memory │ │ Tools │ │ Security │
│ 模型层 │ │ 记忆层 │ │ 工具层 │ │ 安全层 │
└───────┘ └─────────┘ └─────────┘ └──────────┘
│ │ │ │
│ │ │ │
▼ ▼ ▼ ▼
OpenAI SQLite Shell 沙箱/配对
Claude Postgres 文件读写 访问控制
Gemini Markdown 浏览器 密钥加密
Ollama 向量搜索 HTTP请求 速率限制
... ... 硬件控制 ...
│
▼
┌──────────────┐
│ Peripheral │
│ 硬件外设层 │
│ STM32 · RPi │
└──────────────┘
核心设计思想:Trait + Factory 模式
用大白话解释
想象你在拼乐高。每种积木(Provider、Channel、Tool……)都有统一的接口形状,只要形状对了就能插上去用。你不需要关心积木内部是什么颜色什么材料——OpenAI 和 Ollama 都是"模型积木",Telegram 和 Discord 都是"频道积木",插上去就能跑。
这就是 ZeroClaw 的 Trait + Factory 模式:
- Trait(特征)= 定义接口形状,规定"你必须能做什么"
- 实现= 不同厂商/平台各自实现这个接口
- Factory(工厂)= 根据配置文件,选择对应的实现,组装好交给 Agent 使用
配置文件写 "provider = openai"
↓
Factory 读取配置
↓
创建 OpenAIProvider(实现了 Provider trait)
↓
Agent 只通过 Provider trait 接口调用,完全不知道底层是 OpenAI 还是 Ollama
这种设计的好处:
- 换模型:改一行配置就行,代码不用动
- 加新平台:实现 trait、注册到 factory,搞定
- 测试方便:用 mock 实现替换真实服务
- 不怕锁定:任何组件都可以替换
七大子系统详解
1. Provider(模型提供者层)
一句话:负责跟各种大语言模型"说话"。
Provider trait 定义了与 LLM 交互的所有方法:
pub trait Provider: Send + Sync {
// 最简单的聊天:发一句话,回一句话
async fn simple_chat(&self, message, model, temperature) -> Result<String>;
// 带系统提示的聊天(给模型设定角色)
async fn chat_with_system(&self, system_prompt, message, model, temp) -> Result<String>;
// 带历史记录的多轮对话
async fn chat_with_history(&self, messages, model, temp) -> Result<String>;
// 完整请求(支持工具调用)
async fn chat(&self, request, model, temp) -> Result<ChatResponse>;
// 流式输出(边生成边返回)
fn stream_chat_with_system(...) -> BoxStream<StreamChunk>;
// 能力声明
fn supports_native_tools(&self) -> bool; // 是否原生支持函数调用
fn supports_vision(&self) -> bool; // 是否支持图片理解
fn supports_streaming(&self) -> bool; // 是否支持流式输出
}
已实现的 Provider(11 个):
| Provider | 说明 | 原生工具调用 | 视觉 | 流式 |
|---|---|---|---|---|
| OpenAI | GPT 系列 | ✅ | ✅ | ✅ |
| Anthropic | Claude 系列 | ✅ | ✅ | ✅ |
| Gemini | Google AI | ✅ | ✅ | ✅ |
| OpenRouter | 200+ 模型路由 | ✅ | ✅ | ✅ |
| Bedrock | AWS 托管 | ✅ | ✅ | ✅ |
| Copilot | GitHub Copilot | ✅ | ❌ | ✅ |
| Ollama | 本地模型 | ✅ | ✅ | ✅ |
| GLM/智谱 | 中文大模型 | ✅ | ❌ | ❌ |
| Compatible | 任意 OpenAI/Anthropic 兼容接口 | ✅ | ✅ | ✅ |
| Codex | OpenAI Codex | ✅ | ❌ | ✅ |
| Reliable | 弹性包装器(重试/超时/降级) | - | - | - |
工具调用的两种模式:
- 原生模式(Native):模型本身支持 function calling,直接用 JSON 格式发送工具定义
- 提示引导模式(Prompt-Guided):模型不支持原生工具调用,把工具描述注入到提示词里,模型用
<tool_call>XML 标签返回调用请求
Reliable 包装器是个特别设计——它不是一个 Provider 实现,而是一个"套壳":
用户请求 → Reliable → 主 Provider
↓ 失败?
重试 3 次
↓ 还是失败?
切换到备用 Provider
↓ 还是失败?
返回错误
2. Channel(消息频道层)
一句话:负责连接各种聊天平台,收消息、发消息。
pub trait Channel: Send + Sync {
fn name(&self) -> &str; // 频道名称
async fn send(&self, message: &SendMessage); // 发消息
async fn listen(&self, tx: Sender<ChannelMessage>); // 监听消息(长期运行)
async fn health_check(&self) -> bool; // 健康检查
// 可选功能
async fn start_typing(&self, recipient: &str); // 显示"正在输入..."
async fn send_draft(&self, message) -> Option<String>; // 发送草稿
async fn update_draft(&self, recipient, msg_id, text); // 更新草稿(流式输出效果)
async fn finalize_draft(&self, recipient, msg_id, text); // 定稿
async fn add_reaction(&self, channel_id, msg_id, emoji); // 添加表情回应
}
已实现的 Channel(25 个):
| 类别 | 频道 |
|---|---|
| 即时通讯 | Telegram、Discord、Slack、Mattermost、Matrix、IRC |
| 企业办公 | Lark/飞书、钉钉、NextCloud Talk |
| 社交平台 | WhatsApp(Cloud API + Web)、Signal、QQ、iMessage |
| 开放协议 | Nostr、Linq(Web3)、ClawdTalk |
| 传统通信 | Email(SMTP/IMAP) |
| 本地 | CLI(命令行交互) |
| 通用 | Webhook(HTTP POST) |
| 辅助 | Transcription(语音转文字) |
消息流转过程:
Telegram 收到消息 "帮我查天气"
↓
TelegramChannel.listen() 解析消息
↓
封装成 ChannelMessage { sender, content, channel: "telegram", ... }
↓
通过 mpsc 通道发给 Agent
↓
Agent 处理完毕
↓
TelegramChannel.send() 发回去
安全默认值:所有频道的 allowed_users 默认为空列表 = 拒绝所有人。你必须明确配置允许谁跟 Agent 聊天。
3. Tool(工具层)
一句话:Agent 的"手",能执行各种具体操作。
pub trait Tool: Send + Sync {
fn name(&self) -> &str; // 工具名
fn description(&self) -> &str; // 功能描述(给 LLM 看的)
fn parameters_schema(&self) -> serde_json::Value; // 参数的 JSON Schema
async fn execute(&self, args: Value) -> Result<ToolResult>; // 执行
}
pub struct ToolResult {
pub success: bool, // 是否成功
pub output: String, // 输出内容
pub error: Option<String>, // 错误信息
}
已实现的工具(38 个):
| 类别 | 工具 | 说明 |
|---|---|---|
| 文件操作 | file_read、file_write、file_edit | 读/写/编辑文件 |
| Shell | shell | 执行命令行命令 |
| 定时任务 | cron_add、cron_list、cron_run、cron_update、cron_remove | 管理定时任务 |
| 记忆 | memory_store、memory_recall、memory_forget | 存/取/删记忆 |
| 网络 | http_request、web_search、content_search | HTTP 请求、网页搜索 |
| 浏览器 | browser | WebDriver 浏览器自动化 |
| 视觉 | screenshot、image_info、pdf_read | 截图、图片分析、PDF 阅读 |
| Git | git_operations | Git 操作 |
| 硬件 | hardware_board_info、hardware_memory_map、hardware_memory_read | 硬件信息和内存读取 |
| 集成 | composio | 1000+ OAuth 应用集成 |
| 任务 | delegate | 子 Agent 委派 |
| 通知 | pushover | 推送通知 |
| 搜索 | glob_search、cli_discovery | 文件搜索、CLI 发现 |
| 配置 | model_routing_config、proxy_config | 运行时配置修改 |
| 调度 | schedule | 任务调度 |
安全设计:
- 所有文件路径都经过规范化处理,防止符号链接逃逸
- Shell 命令受
allowed_commands白名单控制 - 工具输出会经过凭据清洗(正则匹配 token/key 并脱敏)
workspace_only = true时只能访问工作区目录
4. Memory(记忆层)
一句话:Agent 的"大脑存储",让 AI 能记住之前的对话和知识。
pub trait Memory: Send + Sync {
async fn store(&self, key, content, category, session_id); // 存入记忆
async fn recall(&self, query, limit, session_id) -> Vec<MemoryEntry>; // 召回相关记忆
async fn get(&self, key) -> Option<MemoryEntry>; // 精确查找
async fn list(&self, category, session_id) -> Vec<MemoryEntry>; // 列出记忆
async fn forget(&self, key) -> bool; // 遗忘
async fn count(&self) -> usize; // 记忆总数
}
记忆分类:
enum MemoryCategory {
Core, // 核心知识(持久保存)
Daily, // 日常信息(按天组织)
Conversation, // 对话记录(按会话隔离)
Custom(String), // 自定义分类
}
后端实现:
| 后端 | 关键字搜索 | 向量搜索 | 混合搜索 | 说明 |
|---|---|---|---|---|
| SQLite(默认) | ✅ FTS5 BM25 | ✅ 余弦相似度 | ✅ | 无外部依赖,功能最全 |
| PostgreSQL | ✅ | ❌ | ❌ | 远程存储,适合团队部署 |
| Markdown | ❌ | ❌ | ❌ | 文件系统,最简单可移植 |
| Lucid | ✅ | ✅ | ✅ | 外部嵌入服务桥接 |
| None | - | - | - | 无记忆模式 |
混合搜索原理(SQLite 后端):
用户问 "怎么配置 Telegram?"
↓
关键字搜索(FTS5 BM25)──── 得分 0.7,权重 0.3 ──┐
├── 综合排序
向量搜索(余弦相似度)──── 得分 0.9,权重 0.7 ──┘
↓
最终得分 = 0.7×0.3 + 0.9×0.7 = 0.84
5. Security(安全层)
一句话:多层防护,确保 Agent 不干坏事、不被滥用。
安全设计遵循纵深防御原则——不是靠一道墙,而是好几道关卡:
第 1 层:网关配对认证
└── 6 位一次性配对码 → Bearer Token(类似蓝牙配对)
第 2 层:频道白名单
└── 默认拒绝所有人,必须明确配置 allowed_users
第 3 层:自治级别控制
└── readonly(只读)→ supervised(监督)→ full(完全自主)
第 4 层:文件系统隔离
└── workspace_only=true + forbidden_paths 黑名单 + 符号链接检测
第 5 层:命令白名单
└── allowed_commands = ["git", "npm", "cargo"](只能跑这些)
第 6 层:沙箱
└── Landlock(Linux 内核级)/ Bubblewrap / Firejail
第 7 层:密钥加密
└── ChaCha20-Poly1305 加密存储,日志自动脱敏
第 8 层:速率限制
└── 防止滥用和 DDoS
第 9 层:紧急停止(E-Stop)
└── zeroclaw estop kill-all(一键停止所有操作)
6. Gateway(网关层)
一句话:Agent 的"门口",负责接收外部请求和管理连接。
基于 Axum 框架构建的 HTTP 服务器:
API 端点:
GET /health → 健康检查(无需认证)
POST /pair → 配对认证(X-Pairing-Code 头)
POST /webhook → 发送消息(Bearer Token 认证)
GET /whatsapp → Meta Webhook 验证
POST /whatsapp → WhatsApp 消息接收(HMAC-SHA256 签名校验)
WS /ws → WebSocket 连接(流式响应)
GET /sse → Server-Sent Events(流式推送)
安全默认值:
- 默认绑定
127.0.0.1:42617(只接受本地连接) - 拒绝绑定
0.0.0.0,除非同时开启allow_public_bind = true和隧道 - 配对认证默认开启(
require_pairing = true) - 支持幂等键(
X-Idempotency-Key),防止重复处理
7. Peripheral(硬件外设层)
一句话:让 Agent 能控制真实世界的硬件设备。
pub trait Peripheral: Send + Sync {
fn name(&self) -> &str; // 外设名
fn board_type(&self) -> &str; // 板子型号,如 "nucleo-f401re"
async fn connect(&mut self); // 连接
async fn disconnect(&mut self); // 断开
async fn health_check(&self) -> bool;
fn tools(&self) -> Vec<Box<dyn Tool>>; // 暴露工具给 Agent
}
支持的硬件:
| 板子 | 连接方式 | 说明 |
|---|---|---|
| STM32 Nucleo | USB 串口 | 嵌入式开发板 |
| Raspberry Pi | GPIO (rppal) | 单板计算机 |
| ESP32 | 串口 | WiFi 微控制器 |
| Arduino | 串口 | 入门开发板 |
设计思路:外设通过 tools() 方法暴露操作能力。Agent 并不直接操作 GPIO,而是通过工具接口间接控制——这样 Agent 只需要知道"点亮 LED"这个工具怎么用,不需要懂底层电路。
数据流:一条消息的完整旅程
以 "帮我用 Python 写个 Hello World 并保存到 hello.py" 为例:
[1] 用户在 Telegram 发送消息
↓
[2] TelegramChannel.listen() 接收,封装为 ChannelMessage
↓ mpsc 通道
[3] Agent 编排循环收到消息
↓
[4] Memory.recall("Python Hello World") → 召回相关记忆(如果有)
↓
[5] 构建系统提示词(身份 + 技能 + 上下文 + 可用工具列表)
↓
[6] Provider.chat(请求, 工具定义) → 发给 LLM
↓
[7] LLM 返回 ChatResponse {
tool_calls: [
ToolCall { name: "file_write", arguments: {"path": "hello.py", "content": "print('Hello World')"} }
]
}
↓
[8] 工具调度器执行 file_write
- 路径规范化检查(防止路径穿越)
- workspace_only 校验
- 写入文件
- 返回 ToolResult { success: true, output: "已写入 hello.py" }
↓
[9] 工具结果反馈给 LLM:"我已经把文件写好了,请回复用户"
↓
[10] LLM 返回最终文本:"已经帮你创建了 hello.py 文件,内容是..."
↓
[11] 凭据清洗(扫描输出中是否有泄露的 token/密钥)
↓
[12] TelegramChannel.send() 发回给用户
↓
[13] Memory.store() 自动保存本次对话(如果 auto_save = true)
Agent 编排循环详解
src/agent/loop_.rs 是整个系统的心脏,196KB 的核心编排逻辑:
┌──────────────────────┐
│ 收到用户消息 │
└──────────┬───────────┘
↓
┌──────────────────────┐
│ 记忆召回 │
│ (找到相关历史上下文) │
└──────────┬───────────┘
↓
┌──────────────────────┐
│ 构建系统提示词 │
│ 身份 + 技能 + 工具 │
└──────────┬───────────┘
↓
┌────────────────────────────────────┐
│ LLM 调用 │
│ 原生工具调用 / 提示引导工具调用 │
└────────────────┬───────────────────┘
↓
┌────────────────────┐
│ 响应解析 │
│ 纯文本 or 工具调用? │
└──────┬───────┬─────┘
纯文本 │ │ 工具调用
↓ ↓
┌──────┐ ┌──────────────┐
│ 发送 │ │ 执行工具 │
│ 回复 │ │ (最多10轮迭代) │
└──────┘ └──────┬───────┘
↓
工具结果反馈给 LLM
↓
回到 "LLM 调用" ↺
关键参数:
DEFAULT_MAX_TOOL_ITERATIONS = 10:防止工具调用死循环- 历史消息超过 50 条自动压缩,保留最近 20 条
- 流式输出时,最少 80 字符才更新一次草稿(避免频繁刷屏)
- 凭据清洗正则自动脱敏 token/key/secret
配置系统
ZeroClaw 使用 TOML 格式配置,50+ 个配置段,核心结构:
# === 基础设置 ===
api_key = "sk-..." # API 密钥
default_provider = "openai" # 默认模型提供者
default_model = "gpt-4" # 默认模型
default_temperature = 0.7 # 创造力参数
# === 记忆 ===
[memory]
backend = "sqlite" # 后端:sqlite/postgres/markdown/none
auto_save = true # 自动保存对话
embedding_provider = "none" # 向量嵌入提供者
vector_weight = 0.7 # 向量搜索权重
keyword_weight = 0.3 # 关键字搜索权重
# === 安全 ===
[autonomy]
level = "supervised" # 自治级别
workspace_only = true # 限制在工作区
allowed_commands = ["git", "npm"] # 命令白名单
forbidden_paths = ["/etc", "~/.ssh"] # 路径黑名单
# === 网关 ===
[gateway]
port = 42617
host = "127.0.0.1"
require_pairing = true
allow_public_bind = false
# === 频道 ===
[channels_config.telegram]
bot_token = "..."
allowed_users = [] # 空 = 拒绝所有
# === 运行时 ===
[runtime]
kind = "native" # native 或 docker
热重载:修改 provider/model/temperature/reliability 配置后,下一条消息自动生效,无需重启。
技术选型理由
为什么选 Rust?
| 需求 | Rust 的优势 |
|---|---|
| 性能 | 零开销抽象,无 GC 停顿 |
| 内存 | 编译期内存安全,无运行时开销 |
| 二进制大小 | 8.8MB 静态链接,适合嵌入式 |
| 安全 | 类型系统防止内存错误和数据竞争 |
| 并发 | tokio 异步运行时,高效处理并发 |
| 可靠性 | Result 类型强制错误处理 |
关键依赖选择
| 场景 | 选择 | 原因 |
|---|---|---|
| 异步运行时 | tokio | 生态最完善,多线程调度 |
| HTTP 服务器 | axum | 类型安全、零开销、tower 生态 |
| HTTP 客户端 | reqwest + rustls | 不依赖 OpenSSL,可交叉编译 |
| 序列化 | serde | Rust 标准,零拷贝反序列化 |
| 数据库 | SQLite (bundled) | 零配置,嵌入式部署友好 |
| 加密 | chacha20poly1305 | 现代密码学,比 AES 更适合软件实现 |
| CLI | clap | 自动生成帮助文档和补全 |
明确不选的依赖
| 不选 | 原因 |
|---|---|
| LangChain | Python 生态,性能和内存开销不可接受 |
| Pinecone/Elasticsearch | 重型外部依赖,违反"10 美元硬件能跑"目标 |
| OpenSSL | 不利于交叉编译和安全审计 |
| Electron/Web UI | 二进制膨胀,不符合极简目标 |
性能指标
| 指标 | 数值 |
|---|---|
| 二进制大小 | ~8.8MB(stripped release) |
| 运行时内存 | <5MB(基础启动) |
| 启动时间 | <100ms |
| 支持架构 | x86_64, aarch64, armv7 |
| 支持平台 | Linux, macOS, Windows |
| 最低硬件 | 树莓派 Zero / $10 开发板 |
编译优化策略(Cargo.toml):
[profile.release]
opt-level = "z" # 优化体积(不是速度)
lto = "fat" # 全量链接时优化
codegen-units = 1 # 单线程编译(适配低内存设备)
strip = true # 去除调试符号
panic = "abort" # 更小的 panic 处理器
可观测性
pub trait Observer: Send + Sync + 'static {
fn record_event(&self, event: &ObserverEvent); // 记录事件
fn record_metric(&self, metric: &ObserverMetric); // 记录指标
fn flush(&self); // 刷新缓冲
}
事件类型:AgentStart、LlmRequest、LlmResponse、ToolCall、ChannelMessage、HeartbeatTick、Error 等
指标类型:RequestLatency(延迟)、TokensUsed(Token 用量)、ActiveSessions(活跃会话)、QueueDepth(队列深度)
实现:Log(本地日志)、Prometheus(指标导出)、OTEL(OpenTelemetry 全链路追踪)、Multi(聚合多个观察器)
Feature Flags(功能开关)
ZeroClaw 用 Cargo feature flags 控制可选功能,按需编译,不用的不打包:
| Feature Flag | 说明 | 默认 |
|---|---|---|
hardware |
USB 设备发现 | ❌ |
channel-matrix |
Matrix E2EE | ❌ |
channel-lark |
飞书/Lark | ❌ |
memory-postgres |
PostgreSQL 后端 | ❌ |
observability-otel |
OpenTelemetry | ❌ |
peripheral-rpi |
树莓派 GPIO | ❌ |
browser-native |
WebDriver 浏览器 | ❌ |
sandbox-landlock |
Linux 内核沙箱 | ❌ |
rag-pdf |
PDF 提取 | ❌ |
whatsapp-web |
WhatsApp Web QR | ❌ |
probe |
STM32 内存探测 | ❌ |
这意味着在树莓派上部署时,你可以只编译核心功能,二进制更小;在服务器上部署时,打开所有 feature,功能更全。
总结
ZeroClaw 的架构可以用三个关键词概括:
- 模块化——7 个核心 trait 定义了清晰的扩展边界,任何组件都可以独立替换
- 安全优先——9 层安全防护,默认拒绝一切,需要明确授权
- 极致精简——8.8MB 二进制、<5MB 内存、$10 硬件就能跑,没有多余的依赖