ZeroClaw 插件 ABI v0 完全指南:基于 WIT 的 WASI 组件模型插件协议、版本化策略与迁移实践 📅 发布时间:2026/9/20 9:58:36 👁 浏览次数: ZeroClaw 插件 ABI v0 完全指南基于 WIT 的 WASI 组件模型插件协议、版本化策略与迁移实践【免费下载链接】zeroclawFast, small, and fully autonomous AI personal assistant infrastructure, any OS, any platform — deploy anywhere, swap anything 项目地址: https://gitcode.com/gh_mirrors/ze/zeroclaw导读wit/v0是 ZeroClaw 全自主 AI 个人助理基础设施的插件 ABI 定义目录它用 WITWebAssembly Interface Types描述了一整套运行在 WASI 组件模型之上的插件协议tool工具、channel消息渠道、memory记忆后端三类插件世界的接口、能力位掩码与宿主导入logging / config / secrets / inbound。本文以wit/v0/README.md与同目录下全部.wit接口文件为主体结合zeroclaw-plugins仓的组件宿主实现与测试 fixture系统讲解该实验性 ABI 的目录结构、unstable/since版本化生命周期、.frozen稳定性围栏、宿主兼容窗口、三类插件的完整接口契约以及插件作者在 V0 实验期到 0.1.0 稳定版之间的迁移步骤。读完本文你将能理解 ZeroClaw 插件系统的工作方式、按zeroclaw:plugin0.x契约编写和编译 tool/channel/memory 组件并遵循版本策略安全升级。ZeroClaw 插件体系与wit/v0的定位ZeroClaw 是一个快速、小巧、全自主的 AI 个人助理基础设施其插件系统把可执行扩展编译为 WASI 组件WASM 组件模型运行在宿主沙箱内。wit/v0/README.md开门见山给出该目录的状态声明当前版本为zeroclaw:plugin0.xExperimental在 Component Model ABI 正式落地前可以自由修改目录内不存在wit/v0/.frozen标记这是实验期的可验证信号全部内容都门控在plugins-wit-v0feature之后对应各.wit文件中的unstable(feature plugins-wit-v0)注解bindgen!调用方只有显式开启features: [plugins-wit-v0]才能看到这些项。从源码看宿主侧确实按wit/v0路径加载接口在 crates/zeroclaw-plugins/src/component.rs 中宿主通过path: wit/v0引用 WIT 包同文件还内置了WIT 漂移提示WIT_DRIFT_HINT见 component.rs当出现 WIT interface/type mismatch 时最常见的根因是插件自带的一份wit/v0副本与宿主版本发生漂移解决方案是针对该宿主版本随附的 WIT 重新构建——这正是本文第五部分迁移指南的核心诉求。目录布局一个vN/目录对应一个 WIT 包主版本wit/VERSIONING.md定义了整个wit/目录的布局约定wit/ VERSIONING.md ← 版本化策略说明 v0/ ← zeroclaw:plugin0.x实验 → 稳定 .frozen ← v0 稳定化时创建当前不存在 实验期 channel.wit config.wit logging.wit memory.wit plugin-info.wit README.md secrets.wit tool.wit types.wit v1/ ← 未来破坏性变更 → zeroclaw:plugin1.0.0规则很清晰每个vN/目录映射一个 WIT 包主版本小版本0.2、0.3……留在同一目录内用since注解表达。wit/v0内的 9 个文件共同构成zeroclaw:plugin0.1.0这一个 WIT 包。版本化策略什么算破坏性变更什么不算冻结目录frozenvN/中的破坏性变更——必须开新目录vN1/以下任何一项变更都要求新建vN1/目录删除或重命名任何类型、函数、record 字段、enum case、variant case给已有 enum 或 variant 新增 case它们是封闭类型旧组件与新宿主或新组件与旧宿主会链接失败改变任何函数参数或返回值的类型改变任何 record 字段的类型调整 record 字段的顺序。非破坏性变更——留在原目录通过since表达给*-capabilities增加新的flags位给接口增加新的、受能力位门控的函数新增 record / variant / enum 类型但不能给已有 enum/variant 加 case在包内新增 WITinterface定义新增world定义。unstable/since生命周期开发期用unstable(feature your-feature-name)注解。未在bindgen!中开启对应features的调用方看不到该项。发布期移除unstable改为since(version 0.x.0)。未加 feature 门控的bindgen!调用方自动可见该项。目前wit/v0/的全部内容都处于unstable(feature plugins-wit-v0)门控下首个稳定的 Component Model 版本发布时解除。在版本目录冻结之前组件必须针对目标宿主随附的 WIT 重新构建——包括对已有 enum/variant 追加 case 这类变更也会要求重建组件。宿主兼容窗口Host Compatibility Window版本目录冻结后宿主为**当前主版本 前一个主版本N-1**维护适配层该窗口不适用于未冻结的实验版本发布版本受支持被移除V0当前V0—V1V1、V0—V2V2、V1V0移除一个版本需要满足三个条件CHANGELOG 条目、上一版本中的弃用公告以及一条明确指出所检测到 WIT 版本的清晰错误信息。稳定性围栏Stability Fencewit/vN/.frozen在对应版本被宣布稳定时于专门的 PR 中创建。它存在之后wit-breaking-change-checkskill 可以评估任何删除或修改wit/vN/*.wit现有行的 PR只接受增量变更新类型、新函数、since注解围栏有一些自动化能力但仍依赖人工审查reviewer 必须确保 skill 已运行、报告的破坏性变更在合并前被处理。三类插件世界与全部 WIT 接口契约wit/v0定义了tool-plugin、channel-plugin、memory-plugin三个world以及types、plugin-info、tool、channel、config、secrets、logging、memory、inbound九个接口。共享类型types与plugin-infotypes.wit 目前只有一个语义别名/// Semantic alias: a JSON-encoded string value. /// Callers must produce valid JSON; receivers must parse it as JSON. type json-string string;plugin-info.wit 是所有插件类型都必须导出的自标识接口interface plugin-info { /// Return the plugins canonical name as declared by the plugin itself. plugin-name: func() - string; /// Return the plugins version string as declared by the plugin itself /// (e.g. 1.2.3). plugin-version: func() - string; }关键设计点plugin-name/plugin-version报告的是组件自身的身份与旁边的 manifest 相互独立。宿主目前还不会调用这两个导出将来调用时若与 manifest 字段不一致只会产生宿主侧警告而非加载失败。插件作者应保持二者同步。工具插件tool接口与tool-pluginworldtool.wit 定义单工具插件的契约record tool-result { success: bool, output: string, error: optionstring, } /// Tool name used in LLM function calling. name: func() - string; /// Human-readable description forwarded to the LLM. description: func() - string; /// JSON Schema for the tools parameters, encoded as a JSON string. parameters-schema: func() - json-string; /// Execute the tool with the given JSON-encoded arguments. /// Returns an error string on failure. execute: func(args: json-string) - resulttool-result, string; world tool-plugin { import logging; /// Read schema-designated secrets for this admitted tool instance. import secrets; export plugin-info; export tool; }执行模型运行时在加载时调用一次name、description、parameters-schema把工具注册进 agent 循环之后每次调用分发execute。需要注意spec()是宿主侧把上述三个元数据函数组合起来的便捷方法不属于该接口。在真实组件中tests/fixtures/tool-fixture/src/lib.rs 展示了标准的wit_bindgen::generate!用法#[cfg(target_family wasm)] mod component { wit_bindgen::generate!({ path: ../../../../../wit/v0, world: tool-plugin, features: [plugins-wit-v0], }); use exports::zeroclaw::plugin::plugin_info::Guest as PluginInfo; use exports::zeroclaw::plugin::tool::{Guest as Tool, ToolResult}; // ... }该 fixture 还验证了类型化配置契约宿主按 manifest schema 把配置物化为已经是正确类型的 JSON真实 boolean、真实 number而非字符串true、5注入__configguest 端用#[serde(default, deny_unknown_fields)]直接反序列化进类型化结构体一旦宿主注入未在 schema 中声明的键调用即失败——这是对宿主config_schema执行的端到端验证。渠道插件channel接口与channel-pluginworldchannel.wit 是内容最丰富的接口定义了完整的消息模型与能力门控机制。消息与附件类型media-attachmentfile-name、data: listu8原始字节完整跨越 WASM 边界大附件可达数 MB未来修订可能引入 resource-handle 模型、mime-type: optionstringinbound-messageid、sender、reply-target、content、channel旧平台提示字段宿主在路由时忽略改为按被准入的逻辑端点盖章渠道类型、channel-alias同样被忽略并改为按被准入的实例绑定盖章、timestamp: u64Unix 毫秒、thread-ts平台线程标识如 Slackts、interruption-scope-id中断/取消分组用线程作用域 ID顶层消息为none、attachments、subject邮件回复线程用send-messagecontent、recipient、subject、thread-ts、attachments、in-reply-to设置邮件In-Reply-To头。注意RustSendMessage中的cancellation-token被有意省略——它是宿主侧 Rust 概念在插件边界内无意义。审批模型approval-requesttool-name、arguments-summary、JSON 编码的raw-arguments: optionjson-string、position: optionapproval-position描述提交给操作员审批的工具调用approval-position是模型在一轮中发出的整批调用的原始位置index从 1 开始、total为批内数量而非审批计数approval-responsevariant 提供四种应答approve执行、deny拒绝、always-approve执行并加入会话级 allowlist、deny-with-edit(string)拒绝并附上修改后的参数。Webhook 模型webhook-rejection只含两种带公开 HTTP 分类的拒绝unauthorized(string)→ 401、bad-request(string)→ 400字符串是受限的私有诊断上下文只进宿主日志绝不返回给未认证的调用方也不得包含凭据或原始密钥webhook-request携带网关给出的精确元数据method、无前导?的原始query、小写 UTF-8 的headers、body: listu8webhook-response要么是messages(listinbound-message)要么是reply(string)HTTP 200 文本应答不进入发送策略、去重或 agent 投递最多 4096 UTF-8 字节超出返回不透明 502。能力位掩码channel-capabilities运行时在加载时调用一次get-channel-capabilities对未设置的 flag 使用 Rust trait 默认值而不是调用插件。完整默认值表能力位未设置时的 Rust 默认行为health-checktrueself-handlenoneself-addressed-mentionnonedrop-self-messagefalsestart-typing/stop-typingok(())supports-draft-updatesfalsesend-draftok(none)update-draft/update-draft-progress/finalize-draft/cancel-draftok(())supports-multi-message-streamingfalsemulti-message-delay-ms800add-reaction/remove-reaction/pin-message/unpin-message/redact-messageok(())request-approval/request-choiceok(none)supports-free-form-asktruewebhook-pathnoneparse-webhookerr(bad-request(unsupported))强制约定即便 flag 未设置插件仍必须导出所有对应函数stub 实现即可运行时只是不会调用它们。必需方法无 Rust 默认name、configureconfigure: func() - result_, string完整加载时初始化通过config.get读 schema 校验后的公开对象、secrets.get读密钥属性两者共享同一解析出的 canonical revision不得为后续操作保留配置派生值、send、poll-message非阻塞队列空时立即返回none运行时应在调用间让出例如指数退避至多约 50 ms 以避免在阻塞线程池上忙循环、get-channel-capabilities。能力门控方法未设 flag 时运行时不会调用health-check、self-handle机器人自身句柄如my_bot用于运行时自循环防护以丢弃机器人自己发的入站消息、self-addressed-mention用户称呼机器人时使用的提及形式如 Discord 的123456、Telegram 的my_bot会逐字注入按渠道区分的系统提示词、drop-self-message多 agent 自循环防护、start-typing/stop-typing、supports-draft-updates与草稿四件套send-draft返回平台消息 ID 供后续编辑、update-draft、update-draft-progress、finalize-draft、cancel-draft、supports-multi-message-streaming与multi-message-delay-ms多消息模式下段落间最小延迟毫秒数、add-reaction/remove-reaction/pin-message/unpin-message/redact-message、request-approval操作员不实现交互审批时返回none调用方回退到自动拒绝、request-choice多选提问timeout-secs为最长等待超时或渠道不支持时返回none、supports-free-form-ask是否能通过标准 sendpoll 流程处理无选项的ask-user提问、webhook-path网关路由段宿主挂载在/plugin/segment下合法段为 1–64 个 ASCII 字母/数字/连字符/下划线任何其他值都会拒绝该渠道实例由webhook-ingress门控与parse-webhook认证并解码 GET/POST webhook在使用点解析作用域内 config/secrets 并校验平台真实性由webhook-ingress门控。channel-pluginworld 的导入logging、config在使用点读取 schema 校验过的公开配置对象、secrets在实例化与静态元数据发现期间的调用返回unavailable、inbound导出plugin-info与channel。配置与密钥config/secrets宿主导入config.wit 提供宿主中介的、schema 校验过的非密钥部分 canonical 配置读取enum config-error { access-denied, // 被准入实例未持有 config-read 授权 unavailable, // 调用帧未授权 / canonical 配置无法解析 / 调用预算耗尽 } /// 返回当前类型化公开配置对象JSON。 /// 标记为 x-secret: true 的属性被省略。 /// 公开与密钥读取在一次调用中共享同一解析出的 canonical revision。 get: func() - resultjson-string, config-error;secrets.wit 定义密钥读取。安全设计值得注意guest 只提供逻辑属性名宿主根据被准入的插件实例推导出包、能力与绑定因此一个组件无法选择另一个插件的密钥命名空间enum secret-error { access-denied, not-found, // 该名称不是此实例可用的密钥属性 unavailable, } get: func(name: string) - resultstring, secret-error;secrets.get只在宿主分派工具的execute导出或渠道的配置与操作导出期间可用实例化与静态元数据发现期间的调用返回unavailable。安全密钥的 schema 在包签名时被签名覆盖signature-covered。结构化日志logging宿主导入logging.wit 是全部插件类型共用的日志接口关键约束是不要用wasi:logging——否则插件日志的格式化会与其他日志不一致也不会出现在zeroclaw_log写入的全部三处位置必须用log-record保证一致性。该接口镜像zeroclaw-log的Severity与Action枚举log-leveltrace/debug/info/warn/errorplugin-actionstart、complete、fail、cancel、skip、timeout、retry、inbound、outbound、send、receive、connect、disconnect、reconnect、spawn、kill、tick、trigger、schedule、approve、reject、defer、read、write、delete、list-actionRust 侧原名list是保留字故改名、query、invoke、dispatch、resolve、register、unregister、load、save、migrate、validate、note、memory-audit这是封闭分类法有意不提供逃生舱变体plugin-outcomesuccess/failure缺席映射为宿主的EventOutcome::Unknownplugin-eventrecordfunction-name命名空间限定的函数路径如my_plugin::tool::execute、action、outcome: optionplugin-outcome、duration-ms: optionu64、attrs: optionjson-string宿主可按action解析、message必填的可读描述。log-record: func(level: log-level, event: plugin-event)是即发即忘fire-and-forget无返回值宿主静默吸收一切错误——一次失败的日志写入绝不能使插件执行崩溃若活动服务帧已耗尽自定义导入预算宿主也会丢弃该事件。入站队列inbound宿主导入inbound.wit 解释了渠道插件的入站架构渠道插件运行在无网络、无 socket的 WASI 沙箱中无法直接接受入站流量由宿主运行监听器webhook 服务器、厂商隧道、轮询客户端把收到的每条消息投入每插件队列插件通过导出的poll-message调用inbound-poll排空队列。这样网络面留在宿主 Rust 侧插件只负责消息整形与策略。接口提供host-inbound-message与channel接口的inbound-message有意相互独立这样导入inbound不会把导出的channel接口拖入宿主导入图插件自行把这些字段映射到poll-message的返回值inbound-poll: func() - optionhost-inbound-message非阻塞取队首消息inbound-pending: func() - u32当前队列积压数让插件可以成批排空而无需在每两条之间插入一次哨兵空轮询。记忆后端插件memory接口与memory-pluginworldmemory.wit 面向持久化记忆后端模型覆盖分类、向量召回、agent 作用域、GDPR 数据可移植性等。类型memory-categoryvariantcore长期事实/偏好/决策、daily每日会话日志、conversation对话上下文、custom(string)用户自定义类别名memory-entryrecordid、key、content、category、timestampRFC 3339、session-id、score: optionf640.0–1.0 召回相关度非向量召回时缺席、namespaceagent/上下文间隔离、importance: optionf640.0–1.0 优先级权重、superseded-by取代本条的条目 ID、agent-alias可读别名如clamps用于显示/路由、agent-id存储层原始 agent 标识用于作用域相等性判断export-filternamespace、session-id、category、since/untilRFC 3339 时间下/上界含端点用于 GDPR Art. 20 数据可移植性批量导出procedural-messagerole、content、name用于程序性记忆的对话痕迹agent-filtervariantall空切片无 agent 过滤或some(liststring)非空切片只返回列出的 agent ID——对应 Rust[str]切片的映射。必需方法name、get-memory-capabilities、store-entrytrait 原名store是 wit-bindgen 保留类型名故改名、recall空或裸*的query返回最近条目即纯时间召回时间界为 RFC 3339、含端点、get按 key 取单条多行共享 key 时返回任意匹配行agent 级查找用get-for-agent、list-entriestrait 原名list是 WIT 保留关键字、forget删除匹配 key 的所有行返回是否至少删了一行、forget-for-agent只删(key, agent-id)行其他 agent 的兄弟行不动、count、health-check、store-with-agent带完整元数据与显式 agent 归属存储、recall-for-agents。能力位掩码memory-capabilities及其默认回退运行时加载时调用一次get-memory-capabilities能力位未设置时的宿主默认行为get-for-agent宿主组合get agent-id 相等过滤purge-namespace/purge-session/purge-session-for-agent/purge-agent宿主返回 not supportedreindex宿主返回 0store-procedural宿主 no-opensure-agent-uuid宿主原样回显别名recall-namespaced宿主调用recall后按 namespace 后过滤export-entries宿主调用list-entries后过滤store-with-metadata宿主委托store-entry丢弃 namespace/importance能力门控方法stub 默认值get-for-agent→err(not-supported)purge-namespace/purge-session/purge-session-for-agent按agent-id/purge-agent按agent-alias→err(not-supported)均返回删除条数u64reindex→ok(0)重建 FTS 表/嵌入向量返回重处理条数store-procedural→ok(())no-opensure-agent-uuid→ok(alias)SQL 后端返回 UUID 并插入缺失行非 SQL 后端原样回显别名recall-namespaced→err(not-supported)有原生 namespace 支持的后端应覆写以提高效率export-entries→err(not-supported)按创建时间升序排除嵌入trait 原名export是保留关键字store-with-metadata→err(not-supported)trait 原名store冲突此处为带元数据变体。memory-pluginworld只导入logging导出plugin-info与memory。插件作者的迁移指南从实验期到稳定版wit/VERSIONING.md与wit/v0/README.md为插件作者给出了明确的升级路径。当前实验期 V0 的既有破坏pre-stability break由于wit/v0/.frozen尚不存在这是有意的稳定前破坏tool 与 channel 两个 world 现在都导入secrets接口channel world 还导入configchannel 的configure已从configure(config: string)改为configure()——渠道作者必须改写源码在configure期间通过config.get读取类型化公开对象并在每个使用配置的操作导出中再次读取在同一使用点调用secrets.get且不得在 guest 热状态中保留任一值工具作者保留既有的__config注入以及execute期间调用secrets.get的契约安装到宿主前必须针对当前wit/v0/定义重新构建两类组件发布每个重建组件的新 registry digest签名覆盖的 manifest 内容若有变化需重新签名早期实验 world 的预构建组件不再是符合性目标。实验渠道 world 的 webhook 变更实验 channel world 还包含webhook-ingress能力位、webhook-rejectionvariant以及webhook-path/parse-webhook导出。每个渠道组件都必须导出文档规定的 stub即使它不声明 webhook ingress。这一新增改变了生成的组件 ABI因此宿主升级要求非 webhook 渠道组件也要重建。三种升级场景小版本升级0.1 → 0.2重新编译即可。通过since新增的项无需改源码。主版本升级V0 → V1更新package声明为zeroclaw:plugin1.0.0更新 import 路径以引用新接口按 V1 CHANGELOG 条目适配任何重命名/删除的项以wasm32-wasip2为目标重新编译。宿主升级实验期内无兼容窗口任何 WIT 变更包括给已有 enum/variant 追加 case都可能要求重建组件冻结后由第 3 节的 N/N-1 兼容窗口与.frozen围栏接管。工程实践与可验证依据宿主加载路径crates/zeroclaw-plugins/src/component.rs 以path: wit/v0引用 WIT 包并在类型不匹配时给出 WIT 漂移提示component.rs。端到端测试zeroclaw-plugins的集成测试覆盖了完整链路包括 channel_plugin_e2e.rs、reference_plugin_e2e.rs 等tests/fixtures/ 下提供了 tool、channel、egress 等真实可编译的组件 fixture是插件作者最直接的参考实现。约束守则插件的接入规范、安全与签名约定可继续阅读 crates/zeroclaw-plugins/AGENTS.md。小结wit/v0是 ZeroClaw 插件 ABI 的第一块稳定地基当前为实验态通过types、plugin-info、tool、channel、config、secrets、logging、memory、inbound九个接口把工具、消息渠道、记忆后端三类插件统一到 WASI 组件模型上通过unstable/since、.frozen围栏、N/N-1 兼容窗口和破坏/非破坏变更清单为 ABI 的演进划定了清晰边界。对插件作者而言眼下最重要的行动是针对当前宿主随附的wit/v0/重建组件wasm32-wasip2遵循使用点读取 config/secrets、不保留热状态、未实现能力也必须导出 stub的契约并在wit/v0/.frozen出现后只做since增量变更。【免费下载链接】zeroclawFast, small, and fully autonomous AI personal assistant infrastructure, any OS, any platform — deploy anywhere, swap anything 项目地址: https://gitcode.com/gh_mirrors/ze/zeroclaw创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考