先建立四个判断
ZeroClaw 是一套可自行运行的 Agent 基础设施:接消息、调用模型、执行工具、管理上下文,并支持后台任务。理解它,最有效的方法是追踪一次行动如何发生。[03] [07]
运行时和数据存储可由自己掌管;使用云端模型时,组装后的上下文仍会发往模型服务。本地优先不等于全离线。
Channel、CLI、WebSocket、ACP 等路径复用工具循环;入口的身份处理、流式呈现与投递仍有各自生命周期。
模型提出工具调用,审批、路径与命令策略、执行机制决定它是否能落地。自然语言承诺不足以建立权限边界。
公共 traits、编译特性与 Wasm 插件已经存在;runtime 仍承载较多子系统,不能理解成已经完成的纯插件内核。
判断依据:官方架构、请求与通道生命周期,以及 runtime 的过渡性定位。[03] [07] [23] [24]
基于官方仓库 v0.8.5,固定到 cb2b20a9fe59004b7ef03e123942d31f29236d15。核对设计文档、公共接口和关键实现;本地只验证过 CLI 版本与帮助,没有进行模型端到端运行、性能压测或安全审计。文中“建议”是作者推导。
设计的出发点:控制权在谁手里
官方设计哲学有明确的优先顺序:所有权、安全、精简、模型供应商可替换。这组顺序解释了许多技术选择:为什么要有本地配置和存储,为什么需要模型之外的权限系统,以及为什么把外部能力放在接口之后。[01]
- 1You own it
自己掌管程序可以运行在自己的机器上。配置、会话、工作区拥有可定位的存储位置,用户能够备份、迁移与停止运行。
- 2Security first
行动有边界一旦 Agent 能执行命令、访问网络或写文件,它就是有实际权限的进程。执行前的策略判断与执行时的隔离要各负其责。
- 3Minimal
控制能力成本通过 Cargo features 和模块拆分控制构建范围。精简也包括模型看到的工具集合,以及运行时需要长期维护的依赖面。
- 4Provider agnostic
保留模型选择模型接入经由统一接口和配置组合。业务行为仍受模型的工具调用、流式协议和推理能力差异影响。
Rust 带来了什么,又没有带来什么
Rust 的所有权与类型系统适合承载并发任务、连接与共享状态;原生编译和可选特性有利于分发及依赖管理。与此同时,模型是否准确、工具是否授权、网络是否可靠,都需要运行时的显式设计。这是从架构得到的判断,不能由语言选择直接推导为可靠性结论。
Cargo.toml 的默认特性已经包括 agent runtime、部分 channels、ACP bridge、gateway 等。--no-default-features 会改变能力集合;源码中存在的能力,也可能没有编进你手上的发行包。本文不沿用跨版本流传的“几 MB、几毫秒”性能标签。[02]
拆开系统:边缘接入,核心编排
可以把系统看成三部分:入口适配、统一回合引擎、可调用能力。配置、安全与可观测性贯穿其中。
组装上下文,调用模型,解析工具请求,执行权限判断,再把结果送回模型。cron、SOP、subagents 等也由相关运行时子系统承载。
crates/zeroclaw-runtime/这是职责图,箭头表示主要调用方向,不是完整 Cargo 依赖图。v0.8.5 的 runtime 仍直接依赖若干具体实现 crate。[03] [23]
Rust 如何把规则变成接口约束
一个具体例子是 webhook 接入:VerifiedWebhookIngress 保存已经验证过的请求体,parse_messages(self, …) 消费这个对象,只解析同一份 bytes,再产出 VerifiedWebhookMessages 交给 dispatch。私有字段与消费式 API 共同限制调用者跳过验证、另塞一份消息的空间。这比一句“Rust 更安全”更能解释语言与架构怎样配合;该漏斗目前用于对应的 webhook 适配路径,不代表所有外层入口已经统一。[26]
真正的扩展点,是公共契约
zeroclaw-api 把核心能力抽象为 traits。运行时通过接口调用能力,工厂与注册逻辑把配置解析成具体对象。这样可以替换一个模型适配器或记忆后端,而不必重写整个工具循环。
| 契约 | 它负责什么 | 仍需由上层解决 |
|---|---|---|
| ModelProvider | 对话请求、响应、流式能力等模型协议 | 选择哪个 profile、何时回退、真实服务模型归因 |
| Channel | 收消息、发消息、可选草稿与交互能力 | 身份绑定、去重、会话路由、最终投递 |
| Tool | 名称、参数 schema、执行与结构化结果 | 是否允许执行、超时、审批、审计与回传 |
| Memory | 写入、检索、删除以及范围约束接口 | 什么值得记、何时记、如何处理冲突与遗忘 |
公共接口源码:ModelProvider、Channel、Tool;记忆范围由后端与 scoped wrapper 共同实施。[04] [05] [06] [12]
原生 Rust traits 是源码层契约;它们不意味着任意 Rust 动态库都拥有稳定二进制 ABI。面向外部 Wasm 插件的跨语言契约,另由版本化 WIT 接口与 Component Model 承担。
一次请求,怎样变成行动
假设用户说:“读一下项目说明,整理出待办。”它会触发一轮或多轮模型调用。真正重要的是中间的状态转移:谁能提供上下文、谁能批准动作、工具结果如何再次进入模型。
接收、识别与路由
通道适配器将平台消息转换为内部消息。按入口处理配对、授权、去重、工作区和会话绑定,再交给回合执行链路。
交互示意依据统一 turn engine、工具生命周期与入口处理。不同入口不保证完全相同的外层行为。[07] [24]
共享工具循环,减少入口之间的语义分叉
回合执行的大致结构是:准备历史与工具 schema → 调用 provider → 消费响应 → 解析工具请求 → 审批与执行 → 收集结果 → 更新历史 → 判断是否继续。入口可以配置不同的流式输出、超时和审批行为,但共用执行循环有助于让修复覆盖多条路径。[07]
// 概念伪代码,帮助阅读;不是可复制运行的 API
while turn.within_budget() {
response = model.call(history, visible_tools)
if response.is_final() { return response.text }
for call in response.tool_calls {
decision = policy.check(call, caller_scope)
result = execute_if_allowed(call, decision)
history.append_paired(call, result)
}
}
重试的关键,是“已经对外承诺了什么”
Provider 层负责协议与连接;运行时决定整次调用能否重放。未向外发布不可变输出时,部分故障还有重试空间;一旦文本等不可变事件已经发布,回合重放可能制造重复内容,因此该中断分支停止自动整轮重放。草稿可更新与最终事件不可撤回,也不能简单等同。[08] [09] [25]
同理,工具调用发生过网络超时,不能直接推断业务操作失败。若某个工具负责提交订单或发通知,幂等键、业务回执与回读仍需由工具及上层流程设计。统一 Agent 循环并不自动提供跨外部系统的 exactly-once 语义。这是架构层面的推论。
模型可替换,但能力差异仍需显式处理
路由选择、重试策略、profile 构造与“请求模型 / 实际服务模型”归因分属不同层。工具调用协议、图像支持、上下文上限与流式结束标记都可能不同;统一接口减少适配成本,并不抹平这些差异。排障时应追踪实际 provider 和模型,而不能只看用户最初选择的别名。[08]
典型组合是 Router → Reliable → ModelProvider:Router 选择目标路由,Reliable 管理有界尝试与候选,具体 provider 处理协议。流式路径会选择一个合适且未冷却的候选,启动后直接返回其 stream;不能把普通 fallback 配置理解为“流到一半自动无缝换模型”。[29]
“记住了”,究竟存在哪里
上下文里出现过一条信息,并不代表它进入了长期记忆。理解存储归属,比讨论“有没有向量库”更重要。
临时组装
保持消息结构
明确写入
各有生命周期
ZeroClaw 在这些表面之间传递数据,但它们有不同的 owner、保留策略和权限范围。读过文件只是得到工具结果;回忆被注入 prompt 只是提供上下文;二者都不能直接被算作一次长期记忆写入。历史压缩也可能丢失细节,必须维持 tool call 与 tool result 配对。[10]
记忆注入由执行引擎统一决定
记忆后端负责召回;引擎按 TurnOrigin 决定哪些内容可以进 prompt。当前实现中,Cron / Daemon 排除 Conversation 类记忆,SubTurn 不重复注入,缺少安全 session 范围的用户入口也排除 Conversation 类。统一 renderer 再处理相关性、预算和噪声过滤。这让“后台任务能否带入聊天内容”成为可检查的策略,而不是散落在各通道里的字符串拼接。[30]
SQLite 的意义:先把单机闭环做实
内建 SQLite 后端把结构化记录、FTS5 关键词检索与向量检索放在同一实现中。向量路径会扫描候选 embedding 并计算相似度,在混合模式且两路有结果时与关键词结果合并;实现也支持仅关键词或仅向量等路径。它没有因此获得大规模近似最近邻索引。这个选择易于本地运行与备份,规模增长时的查询成本应通过真实数据评估。[11]
AgentScopedMemory 将写入绑定到当前 agent,并在跨 agent recall 时与允许集合求交。范围收紧依赖真实字段和调用链,不只是 prompt 中的一句“不要看别人的资料”。但 agent 范围也不等同于完整的家庭成员或租户权限体系,后者还需要入口身份映射和应用策略。[12]
更深的议题:谁是最终权威 PROPOSED
ADR-010 提出进一步区分会话连续性、经过筛选的长期知识和可重建的外部检索增强:原始会话由 session store 管,精选记忆由配置的权威后端管,增强连接器可以失效而不影响权威写入。在本次固定版本中,这份 ADR 仍标为 proposed。不能把其中全部验收条件写成已经完成的产品能力。[13]
先决定每类数据在哪里生效、由谁纠正,再接入搜索或向量增强。检索命中、模型实际引用、任务结果正确是三个不同状态,应该分别验证。
安全边界,一层解决一类问题
Agent 的风险来自它能做什么,以及外部内容如何影响它。ZeroClaw 把策略检查、执行隔离和证据机制分开;这些机制相互补充,效果仍取决于入口、配置和实际后端。
通道配对、来源授权、agent 与工作区绑定,决定输入进入哪个执行环境。接入一个新通道,也意味着新增一个信任边界。[24]
工具在执行前接受审批与风险判断;路径和命令策略限制可用能力。模型生成工具参数,不会因此获得决定自身权限的权力。[07]
文件、进程、网络和环境隔离取决于平台及后端。配置中选了 sandbox,不代表所有平台上都取得同等约束。[14]
读实现,才能知道沙箱有没有生效
在 detect.rs 中,后端不可用时存在回退路径:native runtime 可能落到 none,Docker runtime 则可保留自己的容器边界。native 的 auto 也不会因为主机安装了 Docker 就一概套上 Docker 沙箱。操作时应核验实际选择的 backend,而不能照搬文档中的通用优先级表。[14]
工具回执能证明的事情,要说准确
实现使用随机生成的临时密钥,对工具名、序列化参数、结果与时间戳计算 HMAC-SHA256。模型拿不到密钥,因此难以伪造一个新的有效回执。但校验需要相应运行时的密钥;它不是第三方可独立验证的公证,也不是永久证据链。[15]
| 问题 | 当前边界 |
|---|---|
| 所有调用都有回执? | 成功执行的结果可签发;阻断、拒绝、超时和失败路径主要依赖其他事件证据。 |
| 跨重启还能验证? | 密钥保存在活跃作用域的内存中;作用域结束后,不具备持久的跨作用域验证能力。 |
| 是串起来的审计链? | 回执覆盖单次调用,不与前序会话哈希串联。永久回执数据库仍列为计划项。 |
| 能证明业务已完成? | 只能证明对应结果由持有密钥的运行时签发。结果是否准确、外部服务是否真正完成,仍要看工具协议和业务回读。 |
回执的精确覆盖范围以专门文档与实现为准;概览中的“cryptographically-linked audit log”描述不宜直接照搬。[15] [16]
Rust 内存安全、策略检查、沙箱都不能单独消除 prompt injection。需要把外部检索内容作为不可信数据处理,并限制工具真实能力。本文没有验证所有攻击路径,不作“安全无漏洞”的结论。
两条扩展路线:原生 Rust 与 Wasm
原生能力
实现 Rust trait,通过 registry、factory 与 Cargo feature 接入。适合与运行时深度协作、对性能或系统能力要求较高的模块。
- 通常随主程序重新构建。
- 能力进入宿主进程的信任范围。
- 需要控制依赖方向和编译成本。
外部组件
WIT 定义 tool、channel、memory 等组件契约;Wasmtime 执行组件,bridge 再把它们呈现为原生 trait 实现。
- 接口显式、可版本化、可跨语言。
- host imports 按权限提供。
- 增加 ABI、资源限额与分发管理成本。
ADR-009 已接受 WIT + 直接 Wasmtime 的执行架构,取代早期 Extism 桥接方案。引用旧教程时尤其要注意这一代际变化。[17]
host 通过权限决定是否提供 HTTP 与配置访问,限制内存、表、实例、fuel 和单次调用时间。这里的 fuel 是执行预算,不是模型 token。签名可以验证插件来源与完整性,但无法替代对插件行为和授权范围的判断。[17] [18]
将可变能力移到边缘,可以缩小核心变更面;同时必须维护接口版本、组件生命周期、权限和错误传播。v0.8.5 已具备这些基础,runtime 的进一步拆分仍在进行。
长期运行,难在状态与恢复
一个 Agent 能启动,并不表示它能在进程退出、模型超时或网络中断后正确继续。不同后台任务有不同的恢复合同。
| 机制 | 适合表达什么 | 需要看清的边界 |
|---|---|---|
| Cron | 到时触发的一次工作 | 计划和运行记录持久化,与正在执行的任务能否恢复是两回事。 |
| SOP | 有步骤、状态和审批的工作流程 | v0.8.5 的 persist_runs 默认已为 true;仍需检查具体恢复策略和步骤副作用。 |
| spawn_subagent | 由父任务等待子任务结果 | 该工具直接 await 子运行;名称中的 spawn 不代表必然在后台独立运行。 |
| 后台 delegate | 脱离当前回合的委派执行 | 任务记录可留存,旧进程任务可标 lost;这不等于重启后自动恢复执行。 |
后台文档用于理解职责;SOP 默认值以 schema 实现为准,避免沿用该文档仍残留的旧默认描述。同步子任务以实际 await 调用为准。[19] [20] [28] [32]
恢复能力,需要分层看
SOP 默认选择持久后端,但初始化失败时会告警并退回内存,以保住 daemon 启动。后台 delegate 的旧进程任务可以被 reaper 标记为 lost,这提供中断可见性,却不等于重新创建执行栈。记录留存 → 中断可发现 → 流程状态可恢复 → 副作用可安全续跑,是逐层增强的四种能力。[27] [28]
临时 subagent 也有实际的继承约束:策略 override 不能超出父级权限,记忆允许集合只能收紧,动作 tracker 复用父级。它防止“委派一次就重置权限和预算”的放大;仍不能把它当作完整的多人身份治理。[31]
配置也有生命周期
配置通过同一 typed schema 加载,环境覆盖和磁盘持久化有区别;gateway 中保存成功,也不必然意味着 daemon 所有子系统已采用新值。reload 可能在同一 PID 内重建监听器与调度等子系统,独立 gateway 路径还可能需要重启。排查时应分别确认“已保存”和“已应用”。[21]
可观测性首先要能回答:谁做的
日志架构把 attribution(agent、channel、model、session 等归属)与事件属性分开,通过 span 传播上下文。异步 spawn 需要保留这种归因,否则后台日志很容易变成无主事件。直播事件、JSONL 持久化和可选 observer 投影也有不同完整性保证;看到仪表盘事件不等于已经可靠落盘。[22]
如果实际试用,优先验证哪几条链路?
先用一项低风险真实任务验证:消息身份正确 → 工具范围正确 → 结果可回读 → 最终回复送达。随后再验证一次拒绝执行、一次模型或网络失败、一次重启恢复。记录任务结果与恢复行为,比单独数测试数量更能帮助判断是否适合长期运行。
放进个人上下文系统,应该站在哪一层
在“飞书 + 豆包 + OpenViking”这样的组合中,可以把 ZeroClaw 候选定位为执行与编排层:接入消息、调用模型、使用受约束的工具,再把结果送回入口。以下是从职责边界推导的组合建议,不是已完成的集成或部署。
让每个系统承担清晰的职责
豆包模型 API 提供推理;OpenViking 可作为上下文检索服务候选。通过经过验证的适配层连接,明确来源、身份范围、写入权威和结果回执。
| 组件 | 建议职责 | 接入前应验证 |
|---|---|---|
| 飞书 | 成员入口、确认交互、通知呈现 | 账号与 agent 的映射、私聊/群聊范围、消息真实送达。 |
| 豆包 API | 语言理解、整理、推理及工具调用候选 | 实际 endpoint 与认证、工具和流式兼容性、费用与限额。豆包 App 的登录状态不等于 API 凭据。 |
| OpenViking | 带来源和权限范围的上下文服务候选 | 检索引用、召回范围、删除与更新传播、失败降级;不能假定已有原生适配。 |
| ZeroClaw | 统一执行回合、工具约束与后台流程 | 发行包 feature、真实 sandbox 后端、恢复语义及端到端结果。 |
从一个闭环开始,才能判断是否值得接入
一个合适的试验是:“从一份获准资料生成摘要,在指定会话中返回带来源的结果。”先验证资料访问、检索、回答与投递,再增加后台提醒或共享写入。家庭多人场景还需要成员授权、事项账本、冲突处理和撤回传播;这些应用语义不会因为装上 Agent runtime 自动出现。
入口负责身份,模型负责推理,
运行时负责行动,数据系统负责权威。
继续读源码:一条短路径
- 先看 Cargo.toml 与 zeroclaw-api。建立构建范围和公共接口地图。
- 再追 agent/turn。从一次 provider call 跟到审批、工具结果与下一轮。
- 对照 memory 与 security。确认数据范围和行动权限在哪里落实。
- 最后看 plugins、cron / SOP 与 config reload。理解扩展及持续运行的成本。
以下 32 处引用全部固定到本文 commit。线上 master 文档会继续变化;重跑或部署时,应以目标版本的源码、配置和实际行为为准。文章是技术分析,未与项目官方建立关联。
- 01设计哲学与优先级docs/book/src/philosophy/index.md
- 02Workspace 与编译特性Cargo.toml
- 03架构总览与演进方向docs/book/src/architecture/overview.md
- 04公共接口:ModelProvidercrates/zeroclaw-api/src/model_provider.rs
- 05公共接口:Channelcrates/zeroclaw-api/src/channel.rs
- 06公共接口:Toolcrates/zeroclaw-api/src/tool.rs
- 07一次请求的完整生命周期docs/book/src/architecture/request-lifecycle.md
- 08Provider 路由与重试分层docs/book/src/architecture/provider-routing-lifecycle.md
- 09流式输出的提交边界crates/zeroclaw-runtime/src/agent/turn/stream_consume.rs
- 10记忆、会话与载荷归属docs/book/src/architecture/memory-payload-lifecycle.md
- 11SQLite 混合检索实现crates/zeroclaw-memory/src/sqlite.rs
- 12AgentScopedMemory 范围约束crates/zeroclaw-memory/src/agent_scoped.rs
- 13ADR-010:记忆权威分离(proposed)docs/book/src/architecture/decisions/ADR-010-memory-authority-boundaries.md
- 14沙箱实际选择逻辑crates/zeroclaw-runtime/src/security/detect.rs
- 15工具回执的生成与校验crates/zeroclaw-runtime/src/agent/tool_receipts.rs
- 16工具回执的覆盖范围docs/book/src/security/tool-receipts.md
- 17ADR-009:WIT 与 Wasmtimedocs/book/src/architecture/decisions/ADR-009-wit-wasmtime-plugin-execution.md
- 18Wasm host 权限与资源限额crates/zeroclaw-plugins/src/component.rs
- 19后台工作的不同生命周期docs/book/src/architecture/background-work-lifecycle.md
- 20SOP 持久化默认值crates/zeroclaw-config/src/schema.rs
- 21配置保存与运行时应用docs/book/src/architecture/config-lifecycle.md
- 22日志架构与归因docs/book/src/architecture/logging.md
- 23运行时的过渡性定位crates/zeroclaw-runtime/AGENTS.md
- 24通道生命周期及迁移边界docs/book/src/architecture/channel-runtime-lifecycle.md
- 25Provider 调用的禁止重放分支crates/zeroclaw-runtime/src/agent/turn/provider_call.rs
- 26Webhook 的消费式验证类型crates/zeroclaw-gateway/src/webhook_ingress.rs
- 27SOP 后端故障与内存降级crates/zeroclaw-runtime/src/sop/mod.rs
- 28后台任务的旧进程状态回收crates/zeroclaw-runtime/src/control_plane/reaper.rs
- 29流式 Provider 的候选选择crates/zeroclaw-providers/src/reliable.rs
- 30记忆注入与 TurnOrigincrates/zeroclaw-runtime/src/agent/memory_inject.rs
- 31Subagent 的权限继承crates/zeroclaw-runtime/src/subagent/mod.rs
- 32spawn_subagent 的同步等待crates/zeroclaw-runtime/src/tools/spawn_subagent.rs