SOP Fan-In:概览
fan-in 是一种外部事件源,用于启动 SOP 运行。每个源通过 dispatch_sop_event 将事件传递给 SOP 引擎,后者会将每个事件与所有已加载 SOP 的触发器逐一匹配,并为匹配的 SOP 启动运行。
一个 ZeroClaw 实例可以一次绑定多个 fan-in:一个 MQTT 主题、一个文件系统路径以及一个 AMQP 路由键都可以在无需单独进程的情况下输入同一个引擎。下面为每个来源提供了专门的指南。
调度是如何工作的
- 单一匹配器路径: 单个匹配器会评估每种触发器类型,因此无论来源如何,匹配行为都相同。
- 运行开始审计: 已启动的运行通过
SopAuditLogger进行持久化。 - 无头执行: 定时任务(cron)触发和 Webhook 投递会驱动其运行直至完成。维护周期会把每个已启动的 cron
ExecuteStep或DeterministicStep交给无头运行驱动器,Webhook 路由也会把每个已启动的ExecuteStep交给它(分发阶段已经执行确定性步骤);驱动器以该 SOP 自身的智能体身份执行该步骤。仍未接入实时驱动器的来源会通过process_headless_results将ExecuteStep操作记录为待处理,而不会执行它们。 - 无头所有权: 无头步骤以步骤
agent指定的代理身份运行,若未指定则回退到 SOP 的agent,且该代理必须已配置并启用。由于不存在可继承身份的环境代理回合,没有所有者的流程会在编写时被拒绝,并在调度时被拒绝执行;它绝不会以其他已配置代理的身份运行。手动 SOP 是唯一允许在没有所有者的情况下保存的情形,因为sop_execute会提供一个所有者;仪表板的运行端点没有对应的代理,因此拒绝启动此类流程。 - 无头工具范围: 无头步骤的回合与交互式回合同样受到限制:SOP 控制工具(
sop_execute、sop_advance、sop_approve)始终被移除,以防止步骤驱动自身所在的运行;当设置了sop.step_scope_enforce时,步骤声明的scope会在此基础上进一步生效。从该步骤派生的子代理同样继承相同的排除项。 - **不受信任的输入:**在到达模型上下文之前,topic 和 payload 文本会被截断、标准化、经过 prompt-guard 筛查并加上边界框架。
源代码
直接从 SopTrigger 注册表投影的每种 SOP 触发器类型、其字段以及其派发状态:
| 类型 | 字段 | 备注 |
|---|---|---|
mqtt | topic,可选的 condition | MQTT 消息到达。Live:由 MQTT 监听器传递。 |
webhook | path | 传入的 HTTP 请求。已上线:网关 /sop/* 和 SOP 优先的 /webhook 路由。 |
cron | expression | 基于时间的触发。实时:由 SOP 维护定时器(daemon / channel-start 路径)分发。 |
peripheral | board、signal,可选的 condition | 硬件信号。已定义并匹配,但没有外围监听器为其提供输入。 |
filesystem | path、可选的 condition、可选的 events | Filesystem 变更。实时:由文件系统监视器传递。 |
calendar | calendar_source,可选 calendar_ids,可选 condition | 日历事件状态。已定义并匹配,但没有轮询器实时提供它。 |
channel | channel、可选 alias、可选 condition | 配置的通道上的入站消息或 forge/platform 事件(telegram、discord、slack、Git 等)。实时:当启用该通道的 SOP dispatch 时,由通道协调器投递。Git forge 生产者会设置形如 <channel>.<alias>:<event_type> 的事件主题,并将 event_type 放入载荷中,因此编写的 condition 可以按类型过滤 forge 事件,而无需第二种触发器形状。 |
manual | 无 | 通过 sop_execute 工具发起的代理运行。不是外部 fan-in。 |
amqp | routing_key,可选 condition | AMQP 投递。Live:由 AMQP 消费者以 SOP 派发模式投递。 |
每个源在侧边栏中都有专属指南。实时源(由正在运行的监听器或经过身份验证的网关请求提供)会在事件到达时启动运行;cron 触发器由守护进程的定期 SOP 维护周期调度;代理发起的运行会在代理轮次内通过 sop_execute 启动;其余已定义但尚未接入的源(外围设备和日历)可以验证并匹配,但目前还没有实时事件源路由到调度器。
安全默认值
| 担忧 | 机制 |
|---|---|
| Webhook 身份验证 | Gateway 配对 Bearer 身份验证,以及可选的 gateway.webhook_secret/X-Webhook-Secret;/sop/* 和 /webhook 共享同一个速率限制器。必须至少配置一个控制项用于 SOP 分发,并且所有已配置的控制项都必须通过验证。单独的 [channels.webhook] 别名密钥永远不会授权这些路由 |
| Webhook 重放保护 | 可选的 X-Idempotency-Key,按 SOP 路径设置命名空间,并在 /sop/* 与 /webhook 之间分别设置命名空间。键会在分派前预留,表示至多尝试一次,而不能证明之前的运行已开始 |
| MQTT 传输 | mqtts:// 搭配 use_tls = true 用于 TLS 传输 |
| Filesystem 根目录 | 宽泛根路径(/、/home、/etc、/var、/proc、/sys、/dev、/tmp)在配置校验时会被拒绝,除非启用 allow_broad_roots;include/exclude glob 的作用范围是事件 |
| Filesystem symlinks | 默认情况下,符号链接事件路径会在读取任何元数据、哈希或内容之前被拒绝;follow_symlinks = true 可选择启用,但仍要求规范化后的目标解析到受监视的根目录内 |
| 不受信任的触发输入 | 主题和载荷文本在进入模型上下文之前会被设定上限、规范化、经过提示词防护筛查并加上边框。 |
| 不安全触发块 | untrusted_input_guard = "block" 会使用 BlockedUnsafe 拒绝不安全的未受信任事件;默认的 warn 会审计并允许。 |
| Cron 验证 | 无效的 cron 表达式在解析和缓存构建期间会以失败关闭方式处理 |
| 无头模式归属 | 无头 SOP 必须声明一个已配置且处于 enabled 状态的所属 agent;编写时会阻止保存,驱动程序也会拒绝执行该步骤,而不会借用其他已配置的 agent。通过仪表盘启动的手动运行同样会被拒绝。 |
| 无头工具范围 | Headless 步骤轮次始终排除 SOP 控制工具,并在 step_scope_enforce 下遵循步骤声明的 scope;从该步骤派生的子代理继承这些排除项 |
| Headless 驱动生命周期 | Cron 和 webhook 驱动程序归启动它们的 daemon 代所有:先排空(30 秒),再中止并等待其退出(5 秒),之后才重新加载配置并重建 SOP 引擎。若某个驱动程序在该宽限期内未到达任何 await 点,它将继续运行并被带入下一代,同时记录日志,而不是被分离(detach)。 |
| 无头调度 | 没有实时驱动日志的数据源会运行进度推进,而不是自动执行 ExecuteStep |
故障排除
| 症状 | 可能的原因 | 修复 |
|---|---|---|
| SOP 从不从实时源开始 | 触发器模式不匹配或 condition 失败 | 验证触发器模式与已送达事件匹配;检查 condition 是否符合负载 |
| SOP 已启动,但某个步骤未执行 | 没有实时驱动(外设、日历)的数据源进入了 ExecuteStep | 使用 cron、webhook 或其他实时数据源触发,运行 ExecuteStep 的代理循环,或设计运行流程在需要审批时暂停 |
| Cron SOP 启动后,每次运行都失败,报错“no owning agent” | 该流程未声明 agent,而无头运行中没有可供继承的 agent。 | 将 SOP.toml 中的 agent(或在步骤上)设置为已配置的代理别名 |
| Cron SOP 每次运行都失败,报错为 “which is disabled” | 所属 agent 的配置中设置了 enabled = false | 重新启用该代理,或将 agent 指向一个已启用的代理 |
Dashboard 运行按钮对手动 SOP 返回 422 | 程序的 execute 步骤未声明 owner,而仪表盘运行时没有可供继承的 agent 轮次。 | 为 SOP.toml 中(或步骤上)设置 agent,或从使用 sop_execute 的 agent 启动该流程 |
| 一个 cron 步骤无法调用其在交互模式下使用过的工具 | the step 的 scope 将其排除在 step_scope_enforce 之外,或者它是 SOP 控制工具 | Widen 步骤的 scope;SOP 控制工具按设计保持排除。 |
| Webhook 触发器从未触发 | 精确触发路径不匹配、SOP 子系统不可用或身份验证被拒绝 | 配置 sop.sops_dir 后运行 zeroclaw daemon,准确匹配完整请求路径,并提供已配置的 bearer/secret 标头 |
| 外设或日历触发器从不触发 | 事件源未连接到调度器 | 使用实时源(Webhook、MQTT、Filesystem、AMQP)或使用 sop_execute 启动运行 |
| Cron 触发器从未触发 | 维护 tick 未运行(未运行 zeroclaw daemon 或 zeroclaw channel start;独立的 gateway start 不会运行该任务),sops_dir 未设置/为空,或 maintenance_interval_secs = 0 | 将 sop.sops_dir 设置为非空值(默认未设置;文档中的值为 shared/sops),并将 sop.maintenance_interval_secs 设置为非零值(默认为 60),然后运行 zeroclaw daemon(或 zeroclaw channel start) |
另见
- 语法:完整的
SOP.toml和SOP.md格式 - SOP 如何运行
- Channels: 概览:MQTT、文件系统和 AMQP 的传输端