SOP 的运行方式
运行时协定
- SOP 定义会从
<shared>/sops/<sop_name>/SOP.toml以及可选的SOP.md中加载。 - CLI
zeroclaw sop用于离线管理定义:list、validate、show、graph、rename、delete。 - SOP 运行由实时事件汇入(经身份验证的 webhook、MQTT、文件系统或 AMQP)启动,也可由守护进程针对
cron触发器定期执行的 SOP 维护周期,或代理内工具sop_execute启动。其余触发器类型(外围设备和日历)已定义并完成匹配,但尚未接入实时事件源(参见 SOP Fan-In)。 - 运行进度使用以下工具:
sop_status、sop_approve、sop_advance。 - 运行状态默认是进程本地的。使用
sop.persist_runs = true时,默认 SQLite 后端成功初始化后,会将其存储在<data_dir>/sop/runs.db下,并在重启后恢复活跃运行。初始化失败会记录警告,并回退到进程本地内存。 - SOP 审计记录以
sop类别存储在配置的 Memory 后端中。
运行状态和审计历史属于不同界面。有关生命周期归属、取消和重启语义,请参阅后台工作生命周期。
事件流
graph LR
MQTT[MQTT listener] -->|topic match| Dispatch
TOOL[sop_execute tool] -->|manual| Dispatch
WH[Webhook request] -->|authenticated HTTP fan-in| Dispatch
CRON[Cron trigger] -->|daemon maintenance tick| Dispatch
GPIO[Peripheral trigger] -.->|defined, unwired| Dispatch
Dispatch --> Engine[SOP Engine]
Engine --> Run[SOP Run]
Run --> Action{Action}
Action -->|ExecuteStep| Agent[Agent Loop]
Action -->|WaitApproval| Human[Operator]
Human -->|sop_approve| Run
开始使用
-
sops_dir默认未设置,因此默认情况下不会加载运行时 SOP。通过网关、zerocode 或zeroclaw config set设置sops_dir即可启用。相对值会根据安装根目录(即存放config.toml的目录)解析,因此文档中所述的shared/sops会解析为<install>/shared/sops,也就是 SOP 作者写入的同一目录。绝对值或以~-前缀的值将按原样使用。将其重新设置为""(或移除)会再次禁用运行时 SOP 加载;CLI 在离线检查时仍会回退到<install>/shared/sops。正在从较早的构建版本迁移? 相对
sops_dir值现在会相对于安装根目录进行解析,与skill-bundles目录的解析方式一致。较早的构建版本对同一设置使用了两个不同的根目录,因此请在升级前检查这两个根目录:在较早的构建版本中显示 它使用的根目录 sops_dir = "shared/sops"最终位于何处运行时加载和本地 zeroclaw sopCLIdata_dir<data_dir>/shared/sopsWeb 和 RPC SOP 编写 <install>/shared<install>/shared/shared/sops(重复的片段)现在两者都解析为唯一的规范路径
<install>/shared/sops。检查两个旧位置,并将其中找到的所有定义移入<install>/shared/sops;升级后,留在任一目录树中的定义都将不可见。通过旧 Web 或 RPC 界面创建的定义最容易遗漏,因为它们位于重复的写入路径中,而不是文档所述的位置。任何其他相对值也会以相同方式移动:
sops_dir = "my-sops"会从<data_dir>/my-sops(运行时和 CLI)或<install>/shared/my-sops(Web 和 RPC 编写)移动到<install>/my-sops。绝对值和以~- 开头的值不受影响。未设置的情况也随之改变:离线 CLI 回退机制之前会扫描
<data_dir>/sops,现在则扫描<install>/shared/sops。zeroclaw sop list读取新位置,因此升级后如果列表为空,说明定义仍位于旧目录树之一中。 -
创建一个 SOP 目录,例如:
~/.zeroclaw/shared/sops/deploy-prod/SOP.toml ~/.zeroclaw/shared/sops/deploy-prod/SOP.md -
验证和检查定义:
sh
zeroclaw sop list zeroclaw sop validate zeroclaw sop show deploy-prod -
触发器通过配置的事件源运行,或通过代理轮次中的
sop_execute手动触发。
重命名 SOP
SOP 的名称即其地址:其定义位于 <sops>/<name>/ 中,每个编写界面都以接收到的名称保存 SOP。若以不同名称保存已编辑的 SOP,会写入一份副本,并使原始 SOP 残留,因此编辑保存会直接拒绝更改名称。重命名是一项独立的操作。
sh
zeroclaw sop rename deploy-prod deploy-production
守护进程通过 sops/rename RPC 方法提供同一操作,参数为 {"from": "...", "to": "..."},也可通过 POST /api/sops/{name}/rename 接口调用,请求体为 {"to": "..."}。如果目标名称已被占用,将返回冲突错误(409,RPC 错误码 -32020);如果源 SOP 不存在,将返回未找到错误(404,RPC 错误码 -32021)。
重命名:
- “只要目标名称已被占用,就会拒绝操作,因此永远无法合并两个 SOP;”
- 拒绝不是单个路径组件的目标,与其他所有接受名称的 SOP 操作执行的检查相同;
- 拒绝不是位于 SOP 根目录中的真实目录的源;若此处为符号链接,则会通过名称检查,随后将所有读写操作重定向到其指向的任意位置,因此重命名检查应不跟随链接,并在读取任何内容之前确认源路径解析后位于根目录之内;
- 重新运行严格的保存校验,因此它无法将创作界面原本会拒绝写入的定义重新写回磁盘;
- 只修改
SOP.toml中[sop] name的值,保留该行自身的注释和空格。步骤、触发器、其他注释以及SOP.md均保持原样;唯一的格式调整是将单引号包裹的名称改为双引号。
目录移动是一次文件系统重命名,并且最后执行,因此在任意时刻 SOP 都只存在于一个位置:被中断的重命名既不会留下两份副本,也不会一份都不剩。如果重命名在清单(manifest)重写与目录移动之间被中断,SOP 仍保留旧目录,而清单已经写入新名称,因此 zeroclaw sop list 会报告新名称。重新执行同一次重命名即可完成剩余操作。
重命名会像保存或删除单个定义一样,直接改写磁盘上的定义。已经加载旧定义的守护进程会继续运行旧定义,直到它重新加载 SOP;运行历史、审计记录以及其他记录了旧名称的内容,仍保留旧名称。
写入操作按顺序串行执行。创建、保存、删除和重命名在处理任何文件之前,都会先获取 SOP 根目录下 .sop-authoring.lock 文件上的建议性锁(advisory lock)。因此即使跨进程,两个写入操作也不会交错执行。如果 CLI 写入的根目录同时有正在运行的守护进程在编写,CLI 会等待轮到自己,而不是与守护进程发生竞争。若编写调用在十秒内无法获取该锁,则直接失败,而不会无限期排队等待。
读取操作不受串行化限制。编写调用写入的每个文件都会通过原子重命名(atomic rename)进行替换,因此读取方不会看到被截断或只写了一半的文件。保存操作会先后写入 SOP.toml 和 SOP.md 两个文件,因此在保存过程中执行的读取可能短暂地看到一个修订版本的清单(manifest)与另一个修订版本的步骤(steps)同时存在。此外,若读取操作与某个新 SOP 的首次保存发生竞争,它可能先看到清单,而步骤尚未存在。保存完成后,下一次读取返回的结果会同时反映两个文件的新修订版本。
每个步骤都通过一次重命名完成提交,因此普通读者和被强制终止的进程都不会看到某个步骤处于半完成状态。重命名之后会对包含被重命名条目的目录执行刷盘(flush),因此在 Unix 上,这一顺序在机器崩溃后依然有效。如果在重命名已经发生之后刷盘失败,操作仍会正常完成,并记录该失败:变更已经完成且可见,只是该条目在崩溃情况下的持久性保证失效。macOS 会通过刷盘来保证顺序,但不会强制清空设备缓存;Windows 没有目录同步原语,因此在这些平台上,保证的最后一环取决于文件系统本身,ZeroClaw 无法强制实现。
有关触发器路由和身份验证详情,请参见 SOP Fan-In。