- 人工智能
- AI Agent
- 交互助手
- 工具调用
- MCP Clients
- 本地部署
- Agent 工作流
- RAG
【免费下载链接】zeroclaw
Fast, small, and fully autonomous AI personal assistant infrastructure, any OS, any platform — deploy anywhere, swap anything 🦀
本文基于仓库内架构决策记录 ADR-016,结合其关联的 zeroclaw-runtime 持有型 crate 契约、FND-003 治理基础文档 与 FND-001 架构路线图,系统讲解 ZeroClaw 中"提取(extraction)是默认、例外必须受控、记录并获准"的工程治理机制。读者将理解:为什么一个无条件禁令会导致三个 PR 用三种方式解决同一问题,例外必须声明哪四项要素,到期(expiry)与审查(review)有何本质区别,以及一条例外如何在代码仓库中留下可审计的记录。
一、背景:什么是"持有型 crate",它为什么需要例外流程
ZeroClaw 正在从单体仓库向微内核架构迁移。FND-001 记录了 v0.7.0 → v1.0.0 的分阶段拆解路线图,目标是将 agent 循环、gateway、通道编排器、daemon、cron、安全、可观测性、硬件、TUI、技能、doctor 等子系统逐一从zeroclaw-runtime中提取为独立 crate 或 WASM 插件。
在拆解完成之前,crates/zeroclaw-runtime被明确标注为transitional holding crate(过渡性持有型 crate),其 AGENTS.md 开篇即声明:
This crate is atemporary holding area, not a permanent home. It contains 126K LOC of subsystems extracted from the original monolith that have not yet been decomposed into their final crate structure.
同时,该契约给出了一条无条件指令:"Do not add new functionality here"。从源码结构看,这条指令指向的正是 crates.md 中列出的 runtime 各子模块:agent/、cron/、daemon/、heartbeat/、skills/、service/、rpc/等,它们都属于等待提取的子系统。
问题恰恰出在"无条件"上。一个普通贡献者遇到的情形是:他负责的子系统仍然住在持有型 crate 里,而它本该迁往的目标 crate 尚未被创建。此时契约禁止了唯一可用的落点,而目的地又不存在——贡献者被夹在中间,只能各自猜测。
二、问题起源:三个 PR,三种解法,三种性质
ADR-016 的 Context 部分用三个真实 PR 展示了同一规则下的三种不同结局,这三种情形"性质不同,不只是规模不同",单一的无条件指令无法区分它们:
1. cron 前置条件门:提取是正确答案(#10220 → #10557)
cron 的前置条件门(precondition gate)先经过了 #10220,然后被提出作为一次性例外,最终在 #10557 中完成了完整提取。
提取是正确的结局,但它是通过构建两套完整方案、然后丢弃其中一套才达到的。ADR 尖锐地指出:
A contributor should be able to establish whether extraction is required before implementing it twice.
即:贡献者应当能在动手实现之前就确定是否需要提取,而不是花两份实现的成本去回答一个本可用简短记录先解决的问题。
2. 共享配置与 agent 生命周期协调:例外有道理,但尚未定案(#10410)
#10410 将共享配置和 agent 生命周期协调代码保留在 runtime 中,而不是在计划中的 daemon 提取之前发明一个 lifecycle crate。
其理由有两层:把代码移到zeroclaw-infra会反转一条已有依赖(因为 config 已经依赖 infra);提前提取会建立一条路线图并不打算要的 crate 边界。
ADR 指出,这只是"在那里应当给予例外的论证",而非已定结论:该放置方式尚未被接受,#10410 需要在本记录建立的流程下获得它自己的明确处置(disposition)。
3. 传输代码没有接收方:退休才是正解(#10179)
#10179 同样撞上了这条规则,但它的传输代码没有任何接收方调用者。ADR 的判断是:在这里,退休(retirement)或显式的所有权决策比任何例外都更合适。
三种情形的对照
| PR / 案例 | 子系统 | 正确处置 | 为什么 |
|---|---|---|---|
| #10220 → #10557 | cron 前置条件门 | 完整提取 | 提取是成比例的(proportionate) |
| #10410 | 共享配置、agent 生命周期 | 待定,需要显式处置 | 立即提取会产生错误边界(wrong boundary) |
| #10179 | 无接收方的传输代码 | 退休 / 显式所有权决策 | 例外无从谈起,因为根本没有调用者 |
核心洞察:"不成比例的重构"(disproportionate refactor)与"错误的边界"(wrong boundary)是两种不同的理由,只是碰巧共享了同一个症状。因此,判断无法被化简为规模阈值——这正是本 ADR 把判断权交给 Core Team、而不是设定一个 LOC 上限的原因。
三、决策:提取仍是默认,例外是有边界的
ADR-016 的 Decision 部分非常简短而清晰:
Extraction remains the default. The Core Team may approve a bounded exception when immediate extraction would require a disproportionate refactor, or would establish a crate boundary the roadmap does not intend.
即:提取仍是默认;当立即提取需要不成比例的重构、或会建立路线图不打算要的 crate 边界时,Core Team 可以批准一个有边界的例外。
这份决策已经在持有型 crate 的契约中落地。crates/zeroclaw-runtime/AGENTS.md 的 "Exceptions" 一节完整复述了这一规则,并链接回本 ADR:
Extraction is the default. The Core Team may grant a bounded exception when immediate extraction would require a disproportionate refactor, or would establish a crate boundary the roadmap does not intend. See ADR-016.
四、一条合格例外必须声明的四项要素
ADR-016 明确规定,获批的例外必须同时点名以下四者——缺一不可:
| 要素 | 含义 | 为什么必须 |
|---|---|---|
| 许可范围(Permitted scope) | 例外覆盖的具体路径 | 必须是具体路径,而不是抽象意义上的"某个子系统" |
| 预期目的地(Intended destination) | 代码预计迁往的 crate | 使例外描述的是"延迟"而非"逆转" |
| 授权者(Approving authority) | 谁批准的 | 责任可追溯 |
| 到期或审查条件(Expiry or review condition) | 什么终结它,或何时重新审议 | 记录必须说明是两者中的哪一个,因为二者行为不同 |
第四项被单独强调:"The record must say which of the two it is, because they behave differently"。这直接引出下一节的两种终结语义。
五、到期(expiry)与审查(review):两种终结方式的本质区别
这是 ADR-016 中最容易被忽略、却最具操作价值的细节。
到期:权限的向前失效
- 到期终结许可。到期后,对已覆盖范围的新增(further additions)需要 Core Team 重新批准。
- 到期不要求移除已依据该例外落地的代码:权限是向前失效(lapses forward),不向后溯及。撤销已接受的东西是提取工作(extraction's job)的职责,而不是某个日期经过的自动后果。
审查:义务的重新审议
- 审查条件本身不终结任何东西。
- 它让 Core Team 承担重新审议的义务,而审议的结果只可能是三种:续期(renewal)、到期(expiry)或提取(extraction)。
用一句话概括:到期是自动失效,审查是触发复审。二者都不等于"到点就回滚"。
六、授予方式:先记录、后合并,与特性 PR 严格分离
ADR-016 对"如何授予"给出两条硬性约束:
- 记录先于特性合并,且与特性合并相互独立。"The record is created before the feature merges, and separately from it."
- 特性 PR 不能给自己授予例外。因为它要豁免的契约,正是约束它自己的那份契约("A feature pull request cannot grant itself an exception, because the contract it would be waiving is the one constraining it")。自己豁免自己,等于契约失效。
此外,例外必须基于具体且有支撑的使用场景:
An exception requires a concrete supported use case. Code with no receiving caller does not qualify; retirement or an explicit ownership decision is the correct answer there.
这与 #10179 案例遥相呼应:没有接收方调用者的代码,正确出口是退休或显式所有权决策,而不是例外。
七、记录方式:active-exception 表就是记录本身
ADR-016 刻意强调,例外的授予方式与仓库中任何其他决策完全相同——通过一条向拥有 crate 的AGENTS.md中"active-exception 表"添加条目的 PR,经正常审查流程并由 Core Team 批准。没有独立机制,也没有正常审查规则之外的批准通道。
关键原则是:
The entry is the record. A decision that exists only in a review thread has not been made, because nothing later reading the contract would find it.
只存在于评审线程里的决策等于没有决策——因为日后阅读契约的人找不到它。这正是本 ADR 把记录物化为一张表的原因。
当前仓库中该表已经存在于 crates/zeroclaw-runtime/AGENTS.md,且表结构完全对应四项要素:
| Scope | Destination | Approved by | Expires or reviewed |
|---|---|---|---|
| (none) |
截至本仓库快照,表中尚无已授予的例外(_(none)_)。这也意味着 ADR-016 的最后一个验收门槛尚未跨过(详见第十节)。
八、例外不是什么:两条不可逾越的红线
ADR-016 对例外的否定边界写得非常明确:
- 例外允许在持有型 crate 已有的子系统上继续工作,但绝不许可在那里引入新子系统("It never permits introducing a new subsystem there")。
- 例外不从一个子系统泛化到另一个子系统("it does not generalise from one subsystem to another")——为 cron 授予一个例外,与 daemon 毫无关系。
这两条红线保证了"有边界"不是空话:每个例外都是点状的、局部的,不会因为一次让步而变成全局的"往 holding crate 里随便加东西"。
九、采纳路径:为什么这是一次治理变更,而非文档编辑
ADR-016 的 Adoption 部分明确指出:本记录把一个无条件禁令变成了在陈述条件下的许可,因此它是一次治理与贡献流程变更(governance and contribution-process change),而不是普通的文档编辑。依据 FND-003 第 8 节,这条路径正是由 RFC 治理循环管辖的。
对照 FND-003 §8 的 RFC 触发条件,本提案命中的正是其中的第二类:
a governance, contribution-process, or project-authority change;
在 FND-003 定义的完整 RFC 生命周期中,这类变更需要经过:公开提案 → 最短 48 小时讨论期(普通 RFC;非常一致同意路径为 72 小时)→ 打开投票(记录不可变快照、活跃选民、阈值、法定人数需两张显式选票、72 小时截止)→ Core Team 以APPROVE/REVISE/REJECT三种方式投票 → 按优先级顺序裁决(返回讨论 / 延迟 / 拒绝 / 接受)。
因此,ADR-016 的采纳是Core Team 的决策,需要显式作出并记录,包括说明走 FND-003 §8 下的哪条路径:
Adopting it is therefore the Core Team's decision to take and record explicitly, including which route under FND-003 §8 applies. This pull request is the concrete proposal, not the adoption. Until that decision is recorded, the unconditional instruction stands and no exception has been granted.
关键状态语义:提交这份 ADR 的 PR 是"具体提案",而非"采纳本身"。在采纳决策被记录之前,无条件指令依然有效,且没有任何例外被授予。这也解释了为什么 ADR 的 status 仍为proposed——它要等采纳与首个例外案例落地。
十、后果与验收:决策前置、债务可见、判断留人
后果(Consequences)
ADR-016 列举了四条后果,构成完整的收益闭环:
- 决策前置:贡献者获得了一条可以在实现之前(而非之后)解决的决策路径。cron 案例花了两次完整实现,才回答了一个简短记录本可先解决的问题。
- 债务可见且带日期:持有型 crate 契约中的 active-exception 表让"累积的债"可读,每条记录都点名"什么终结它",因此一个悄然变得永久的例外是显而易见的,而不是被埋没的。
- Core Team 逐案判断比例性:这是刻意为之。三个案例证明判断无法化简为规模阈值,因为"不成比例的重构"与"错误的边界"是两种不同的理由,只是症状相同。
- 指令保持其效力:本记录没有削弱持有型 crate 的禁令,而是提供了该指令假设存在、却从未定义的流程("it supplies the process the instruction assumed but never defined")。
验收标准(Acceptance)
ADR-016 保持proposed状态,直到满足两个条件:
crates/zeroclaw-runtime/AGENTS.md陈述例外规则并携带 active-exception 表;- 至少有一个例外通过该流程被授予或被拒绝,证明它是可用的、而不只是写在纸上的。
对照 ADR 索引 的当前记录:契约文本与表格已存在(第一个条件在仓库快照中已达成),但表格仍为_(none)_,尚无比照流程授予或被拒的例外,因此第二个条件未满足——ADR-016 在索引中被如实标注为proposed。
十一、延伸:从仓库源码看这个决策的落点
与 ADR-007 的关联
ADR-016 的 frontmatter 声明了relates-to: ADR-007。ADR-007 决定把 gateway 提取为独立的可选zeroclaw-gw进程,并同样保持proposed直到验收边界落地。二者共享同一治理母题:"拆分"是方向,但拆分未完成前,中间态需要明确的处置规则。ADR-007 的提案式完成,恰好是 ADR-016 所描述的"子系统仍住在 holding crate、目标尚未建成"的典型情境。
路线图与子系统清单
FND-001 的 Phase 2–4 路线图定义了完整拆解计划,持有型 crate 契约中照录了等待提取的子系统名单:agent loop、gateway、channels orchestrator、daemon、cron、security、observability、hardware、TUI、skills、doctor——它们将各自进入独立 crate 或被转换为 WASM 插件。从 crates.md 对zeroclaw-runtime的描述可以看到这些子模块的实际存在:cron/、daemon/、heartbeat/、skills/、service/、rpc/等。ADR-016 的意义,正是为这段"子系统在途"的过渡期提供不牺牲架构纪律的合法作业通道。
稳定性定位
契约末尾标注了持有型 crate 的稳定性层级:Experimental——不提供稳定性保证,v0.8.0 起开始分解("Decomposition begins at v0.8.0")。这与 FND-001 的 Phase 2(v0.8.0 "The Runtime",正式确立zeroclaw-runtime为独立可部署单元)时间线一致。换言之:持有型 crate 是过渡状态的产物,例外机制是让过渡状态可管理的治理工具,而不是把过渡状态永久化的后门。
结语
ADR-016 回答了一个几乎所有大型重构都会遇到的现实问题:当"必须拆"遇上"拆不动"时,规则怎么说?它的答案是:提取是默认,例外由 Core Team 授予;例外必须点名范围、目的地、授权者与到期/审查条件;记录先于合并,且只存在于评审线程的决策等于没有决策;例外不引入新子系统、不跨子系统泛化。
这套设计把"一次判断"变成了"一个流程",把"每个人各自猜测"变成了"一张可见、带日期的表",同时让持有型 crate 契约的禁令不仅没有被削弱,反而第一次拥有了它一直在假设却从未定义的执行细则。对于任何正在经历单体拆分、又不想在"宁可拆错"与"随便放放"之间二选一的工程团队,这份 ADR 都是一份值得对照的治理范本。
- 人工智能
- AI Agent
- 交互助手
- 工具调用
- MCP Clients
- 本地部署
- Agent 工作流
- RAG
【免费下载链接】zeroclaw
Fast, small, and fully autonomous AI personal assistant infrastructure, any OS, any platform — deploy anywhere, swap anything 🦀
相关推荐
zeroclaw-runtime 过渡容器 crate 治理指南:从 126K 行单体到微内核的拆分解耦与例外机制
zeroclaw runtime 过渡容器 crate 治理指南:从 126K 行单体到微内核的拆分解耦与例外机制 zeroclaw runtime 是 Zer
人工智能AI Agent交互助手工具调用MCP Clients本地部署Agent 工作流RAGRustFS 全局状态治理:ECStore 全局单例的边界收敛与 Crate 拆分决策
RustFS 全局状态治理:ECStore 全局单例的边界收敛与 Crate 拆分决策 本文是 RustFS 架构治理文档《Global State And C
后端对象存储分布式存储PPSSPP macOS 构建解析:代码签名硬运行时例外与 MoltenVK 更新流程
PPSSPP macOS 构建解析:代码签名硬运行时例外与 MoltenVK 更新流程 macOS/ 目录下的 README.md https://link.g
虚拟化图形学
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考