ZeroClaw Git 事件驱动的 SOP Fan-In:从 Git Forge 事件到 SOP 运行的全链路触发指南
2026/9/19 13:13:50 网站建设 项目流程

ZeroClaw Git 事件驱动的 SOP Fan-In:从 Git Forge 事件到 SOP 运行的全链路触发指南

【免费下载链接】zeroclawFast, small, and fully autonomous AI personal assistant infrastructure, any OS, any platform — deploy anywhere, swap anything 🦀项目地址: https://gitcode.com/gh_mirrors/ze/zeroclaw

导读

本文讲解 ZeroClaw 的Git SOP Fan-In:如何让 GitHub、Gitea、Forgejo 等 Git 平台的仓库事件(PR 打开、Issue 创建、评论、CI 失败、版本发布等)自动启动一条 Standard Operating Procedure(标准操作规程,简称 SOP)运行。你将掌握事件如何从 Forge 路由到 SOP 入口(ingress)、channel触发器如何匹配事件、condition如何精确筛选事件,以及如何配置、触发、排查和审批这类自动化流程。读完本文,你可以直接落地“PR 自动分诊”“CI 失败自动处理”“Issue 自动归类”等事件驱动的自动化运维管线。

本文是 SOP Fan-In 系列中 Git 源头的专门指南。传输层(provider、Forge 认证、轮询、仓库范围、事件路由表)在 Git 通道文档 中配置;本文聚焦于触发器这一侧——哪些事件类型能进入 SOP 入口,完全由通道的逐事件events路由表决定。

一、核心机制:Forge 事件如何变成 SOP 运行

Git Forge 事件可以启动 SOP 运行。当一个事件类型在通道的事件表(eventstable)中被路由到sop时,Git 通道会把标准化后的 Forge 事件提升(lift)为一个 SOP 事件,并派发给 SOP 引擎:

  • 事件主题(topic)git.<alias>:<event_type>,例如git.main:pull_request.opened
  • 结构化 JSON 载荷:携带仓库(repo)、Issue/PR 编号、作者、标题、正文等字段;
  • 分发目标:SOP 引擎(engine)的dispatch_sop_event入口。

从源码看,这条链路在 crates/zeroclaw-channels/src/git/events.rs 的event_to_sop_message中完成:Git 通道把标准化事件包进一个ChannelMessage信封,主题通过ChannelSopTopic::build(channel_key, alias, event_type)生成,载荷由event_payload序列化为结构化 JSON,并设置内部标记internal_sop_event,由编排器(orchestrator)识别并送入 SOP 入口,而不是作为聊天消息进入 LLM 循环。值得注意的是,该内部标记只有 Git 生产者会设置,因此外部用户无法通过伪造消息subject来冒充 SOP 事件。

在 crates/zeroclaw-channels/src/git/channel.rs 的dispatch_event中,路由决策由router::resolve_route(event.event_type(), &self.cfg.events)完成,三种结果分别是:Ignore(静默丢弃)、Message(按对话消息投递)、Sop { sop }(投递到指定 SOP 入口)。

触发与匹配概览

环节机制说明
事件来源Forge REST API 轮询通道按since游标轮询,无需公网 URL 或 Webhook 暴露,可运行在 NAT 之后
事件标准化GitEvent枚举跨 Forge 统一为 9 种事件类型(见下文),与 provider 无关
路由决策events路由表逐事件类型指定messagesop = "<name>"
SOP 入口dispatch_sop_event单一匹配器评估所有已加载 SOP 的触发器,命中即启动运行
主题git.<alias>:<event_type>通道生成的 SOP 事件主题,不是触发器字段
触发器channel触发器匹配通道种类git与可选alias

二、Mention Gate 与作者检查:哪些事件被放行

SOP 路由在闸门上与对话路径有明确差异:

  • Mention 门槛仅对sop路由的生存周期事件放宽:例如一个pull_request.opened被路由到分诊 SOP 后,无论作者是否在评论中@提及应用,都会触发 SOP。这由源码中的gate_mentions参数控制——对话路径传true,SOP 路径传false(见 crates/zeroclaw-channels/src/git/events.rs 与 crates/zeroclaw-channels/src/git/channel.rs)。
  • 作者检查仍然适用于每一次投递(无论 SOP 还是对话):
    • 来自应用自身账户的事件一律丢弃(admit_author中对bot_login的大小写不敏感比较);
    • 来自其他 Bot 的事件按listen_to_bots决定是否放行;
    • 对 actor 的 login 强制实施 peer-group 允许名单(allowlist)。
  • 无作者的事件(例如没有 actor 的 workflow run)直接丢弃,避免绕过作者过滤器。

一句话概括:sop路由绕过mention_only,但不绕过自我/Bot 过滤,也不绕过作者允许名单

相关测试见 crates/zeroclaw-channels/src/git/events.rs:unmentioned_comment_dropped_only_on_the_gated_path(未提及的评论只在门控路径被丢弃,SOP 路径放行)、own_bot_events_always_dropped_even_ungated(自身 Bot 事件即使未门控也始终丢弃)、foreign_bot_respects_listen_to_bots(外部 Bot 遵守listen_to_bots)。

三、触发器与匹配规则(Trigger & Matching)

一个channel触发器匹配:

  • channel:通道种类,固定为git
  • 可选alias:配置的实例名,例如defaultmain
  • 没有topic字段:哪些事件类型能进入 SOP 入口完全由通道的逐事件events路由表决定。

因此:触发器选择来源(source),路由表选择事件(event)。日志中看到的git.<alias>:<event_type>字符串是通道生成的事件主题,不是触发器字段。如果想把触发器限定到单个事件类型,用condition对载荷做测试,例如$.event_type == "pull_request.opened"

事件的结构化 JSON 载荷会被转发进 SOP 事件,可用于可选的触发器condition;步骤上下文(step context)接收的是**截断、净化、加框(capped, sanitized, framed)**后的形式——未受信任的外部文本不会直接拼入模型上下文。一个 JSON-path 形式的condition$.repo == "octo/repo"可以把 SOP 收窄到单个仓库。

已知事件类型

以下 9 种是已知事件类型,与 crates/zeroclaw-channels/src/git/types.rs 中GitEvent枚举的变体一一对应:

事件类型说明载荷关键字段
issue_comment.createdIssue/PR 上的新评论repo,number,comment_id,author,body
issues.opened新 Issue 的开帖repo,number,issue_id,author,title,body
pull_request.opened新 PR 的开帖repo,number,author,title,body
pull_request.closedPR 未合并关闭repo,number,author,title,html_url
pull_request.mergedPR 已合并repo,number,author,title,html_url
pull_request_review_comment.created行内评审评论repo,number,comment_id,author,body
workflow_run.completedCI 工作流运行成功完成repo,run_id,attempt,name,branch,run_number,pr_number,actor,html_url
workflow_run.failedCI 工作流运行失败同上(verdict 为 failed)
release.published发布新版本repo,release_id,tag,name,author,body,html_url

此外,所有 SOP 事件载荷都包含公共字段:source(恒为"channel")、channelgit)、channel_aliasprovider(如github)、sop(目标 SOP 名)、topicgit.<alias>:<event_type>)、event_typededup_idcreated_attarget。这一形状由 crates/zeroclaw-channels/src/git/events.rs 的common_payload构造,并有测试sop_message_carries_reserved_subject_and_structured_payload逐一断言这些字段(见 crates/zeroclaw-channels/src/git/events.rs)。

四、配置实战:把事件路由到 SOP

完整的触发链路由两部分配置组成:通道侧的事件路由+SOP 定义侧的触发器

4.1 通道侧:events路由表

在 Git 通道配置中,为每个事件类型设置路由。GitEventRoute结构定义于 crates/zeroclaw-config/src/schema.rs,只有两个字段:

  • message: bool:把事件作为普通通道消息投递到 agent 循环;
  • sop: Option<String>:把事件路由到指定名称的 SOP 入口。

一个事件条目sopmessage都没设置时,显式禁用该事件类型。配置示例:

[channels.git.main] enabled = true provider = "github" # ... 认证相关配置 ... [channels.git.main.events.pull_request.opened] sop = "pr-triage" # PR 打开 → 分诊 SOP [channels.git.main.events.issues.opened] sop = "issue-triage" # Issue 打开 → 归类 SOP [channels.git.main.events.issue_comment.created] message = true # 普通评论仍走对话路径 [channels.git.main.events.workflow_run.failed] sop = "ci-failure" # CI 失败 → 自动处理 SOP [channels.git.main.events.release.published] message = true # 版本发布通知 agent

路由决策逻辑在 crates/zeroclaw-channels/src/git/router.rs 的resolve_route中:sop非空时优先(即使同时写了message = true),其次message,否则Ignore

关键设计:路由即订阅。通道从路由表推导要轮询哪些 API 端点(TransportPlan::from_routes,见 crates/zeroclaw-channels/src/git/router.rs):评审评论、Releases、Actions 运行等端点只有在其事件类型被路由后才会被轮询。因此:

  • 未配置的通道成本与之前完全一样;
  • 列表workflow_run.failed不会关闭对话(未在非空表中列出的事件类型沿用默认行为:issue_comment.createdissues.openedpull_request.opened按对话投递,其余忽略);
  • 只有被路由的事件类型才被轮询(poll)。

启动时validate_routes(crates/zeroclaw-channels/src/git/router.rs)会对未知事件类型拼写错误和空的sop = ""条目给出启动警告。

4.2 SOP 侧:channel触发器

加载一个 SOP,其channel触发器声明通道(channel = "git",可选alias)。SOP.toml放在sops_dir(默认未设置,需在配置中显式开启,默认文档值为shared/sops)下的子目录中:

# <sops_dir>/pr-triage/SOP.toml [sop] name = "pr-triage" description = "Triage newly opened pull requests" version = "1.0.0" [[triggers]] type = "channel" channel = "git" alias = "main" condition = '$.event_type == "pull_request.opened"'

关于sops_dir:默认未设置(运行时 SOP 执行默认关闭,直到运维显式开启);相对值基于安装根目录解析,文档化值shared/sops解析为<install>/shared/sops。用zeroclaw sop validate <name>校验 SOP。

4.3condition表达式:精确筛选事件

触发器condition与步骤when:守卫共用同一套表达式语法(完整语法见 SOP 语法参考)。触发器条件针对事件载荷求值,fail-closed(条件无效、载荷缺失、JSON path 无法解析、数值比较双方非数字时都视为不匹配);空条件无条件匹配。

常用 Git 事件筛选示例:

表达式效果
$.event_type == "pull_request.opened"只匹配 PR 打开事件
$.repo == "octo/repo"只匹配octo/repo仓库
$.repo == "octo/repo" && ...不支持!单个条件只允许一次比较,无AND/OR/NOT
$.number > 100按编号数值筛选(数值比较)
$.author.login == "dependabot[bot]"按作者 login 筛选

语法要点:

  • $开头的 JSON path 形式:$.path.to.field <op> <value>,点分路径,数组元素用数字段($.readings.1),不支持括号语法;
  • 比较运算符:==!=>>=<<=(单一运算符,解析器最长优先匹配 token);
  • 字符串字面量要加双引号($.status == "critical");JSON 布尔值会被转换为字符串"true"/"false",因此写$.active == "true"
  • 数值比较:两侧都能解析为数字时按数值比较,否则按字符串比较。

五、触发一次运行(Fire it)

按以下三步即可触发一次 SOP 运行:

  1. 路由事件类型到 SOP:在通道配置的events表中写一个带sop = "<name>"的条目;
  2. 加载匹配的 SOP:SOP 的channel触发器声明channel = "git"(可选alias);
  3. 制造 Forge 事件:打开或评论一个 Issue/PR、发布一个 Release、或让一次 workflow run 结束。

通道随后会:标准化事件 → 筛选载荷安全性(作者检查 + peer-group 允许名单)→ 分发给每个已加载且channel触发器匹配、condition(如有)成立的 SOP。路由一个事件类型同时也订阅了对应的 Forge 端点,所以只有被路由的事件类型会被轮询。

排查:如果什么都没启动

症状检查点
事件被当作对话消息而不是 SOP确认事件类型被路由到sop(而不是留在对话默认值)
SOP 触发器不匹配确认 SOP 的channel触发器写了channel = "git"及正确的alias
条件不成立对照载荷核对condition表达式
事件从未到达确认事件类型已在路由表中订阅(路由即订阅),且通道在运行轮询

更多症状与对策参见 Fan-In 总览的排查表。特别地,sop路由仍然受作者允许名单约束——空/错误配置的 allowlist 会吞掉所有事件(包括sop路由的 PR 生命周期事件),首次丢弃会以 WARN 日志提示“dropping events from sender outside the peer allowlist”(见 crates/zeroclaw-channels/src/git/channel.rs)。

六、审批与观察(Approve and observe)

SOP 运行到达检查点(checkpoint)时会暂停为WaitingApproval。两类方式处理:

CLI 方式

zeroclaw sop list # 列出运行状态,找出 WaitingApproval zeroclaw sop approve # 批准暂停的运行 zeroclaw sop pending # 查看所有暂停待审的运行

带外(out-of-band)Gateway API 方式:通过 Gateway API 的审批端点:

  • GET /admin/sop/pending:列出暂停待人工审批的运行(WaitingApproval);
  • POST /admin/sop/approve:批准;
  • POST /admin/sop/deny:拒绝(取消)。

这些路由在 crates/zeroclaw-gateway/src/lib.rs 中注册,处理实现位于 crates/zeroclaw-gateway/src/api_sop.rs。审批端点需要 gateway 配对认证(paired-token bearer 认证),与 Webhook 路由共享限流器。

七、深入原理:从 Forge 事件到引擎分发的完整调用链

把上文各环节串起来,一条pull_request.opened事件的完整路径是:

  1. 轮询:Git 通道按TransportPlan(由路由表推导)轮询 Forge REST API,命中issues端点族(因为pull_request.opened被路由);
  2. 标准化:provider 把原生载荷映射为统一的GitEvent::PullRequestOpened(crates/zeroclaw-channels/src/git/events.rs),事件类型为pull_request.opened,去重 ID 为ghpr_octo/repo#12(PR 没有传输稳定的对象 ID,以owner/repo#number作为身份键);
  3. 路由决策dispatch_event调用resolve_route得到RouteAction::Sop { sop: "pr-triage" }(crates/zeroclaw-channels/src/git/channel.rs);
  4. SOP 事件构造event_to_sop_message生成主题git.main:pull_request.opened、结构化 JSON 载荷与internal_sop_event标记,经通道监听总线送达编排器;
  5. 引擎分发:编排器把事件交给 SOP 引擎的dispatch_sop_event(crates/zeroclaw-runtime/src/sop/dispatch.rs),单一匹配器对每个已加载 SOP 的触发器求值;
  6. 运行启动:匹配成功则启动运行,通过SopAuditLogger持久化运行启动审计;在非 agent 循环上下文中,ExecuteStep动作记录为 pending 而非静默执行(headless 安全,见 Fan-In 总览)。

引擎侧测试dispatch_to_named_sop_filters_matching_channel_triggers(crates/zeroclaw-runtime/src/sop/dispatch.rs)验证了channel = "git"alias = "main"的触发器精确匹配行为。

八、实操示例:一个完整的 PR 分诊 SOP

以下是一个可直接落地的完整示例:收到octo/repo仓库的新 PR 后,自动生成评审摘要并请求人工审批。

1. 通道配置config.toml):

[channels.git.main] enabled = true provider = "github" # App ID / private key 等认证配置见 Git 通道文档 [channels.git.main.events.pull_request.opened] sop = "pr-triage"

2. SOP 清单<sops_dir>/pr-triage/SOP.toml):

[sop] name = "pr-triage" description = "Triage newly opened pull requests in octo/repo" version = "1.0.0" [[triggers]] type = "channel" channel = "git" alias = "main" condition = '$.repo == "octo/repo" && $.event_type == "pull_request.opened"'

注意:condition只允许单次比较,不支持&&。上面的写法会产生无效条件(fail-closed 不匹配)。正确做法是只保留一个条件,或在 SOP 内用步骤when:做多级判断。例如用$.event_type == "pull_request.opened"触发,再在SOP.md步骤中用when:守卫($.steps.1.repo == "octo/repo")过滤。

3. SOP 步骤<sops_dir>/pr-triage/SOP.md):

# PR triage Classify a newly opened PR and prepare an operator-facing summary. ## Steps 1. **Classify** - Inspect the PR payload and extract severity and area. - output: {"type":"object","required":["repo","number","severity"],"properties":{"repo":{"type":"string"},"number":{"type":"integer"},"severity":{"type":"string"}}} - next: 2 2. **Approval gate** - Require explicit approval before any action. - kind: checkpoint - requires_confirmation: true - next: 3 3. **Post summary** - Comment the triage summary back to the PR thread. - kind: capability - capability: forge.comment - with: { repo = $.steps.1.repo, number = $.steps.1.number, body = "Triage summary..." }

forge.comment能力步骤通过 Git 通道的出站路径把评论发回 Issue/PR(provider 无关:GitHub / Gitea / Forgejo),与检查点组合可构成无头(headless)评审管线(详见 SOP 语法参考)。这些注入式适配器只在daemon / channel-start路径注入;独立 agent 运行与 CLI 校验会 fail-closed。

4. 校验与运行

zeroclaw sop validate pr-triage # 校验 SOP zeroclaw daemon # 或 zeroclaw channel start,驱动轮询与 SOP 维护

在仓库打开一个 PR 后,SOP 运行即被触发;到达检查点时用zeroclaw sop list/zeroclaw sop approve或 Gateway API 审批。

九、安全基线

  • 公共仓库的 Issue/PR 评论是不可信输入:保持mention_only = true(对话路径)、用 peer group 门控发送者(空 peer 集合拒绝所有人,["*"]接受任何人),公开仓库把 autonomy 保持在Supervised或更低。
  • SOP 入口对不可信输入有多层防护(见 Fan-In 总览的安全默认值):主题与载荷文本被截断、归一化、prompt-guard 筛查、加框后才进入模型上下文;untrusted_input_guard = "block"可拒绝不安全的不可信事件(BlockedUnsafe),默认warn审计放行;无头分发只记录运行进度而不自动执行ExecuteStep
  • 作者闸门永远生效sop路由跳过的是mention_only,自我/Bot 过滤与作者 allowlist 对每次投递仍然适用。

相关文档

  • Git 通道:provider、Forge 认证、轮询、事件路由
  • Fan-In 总览
  • SOP 语法参考:SOP.toml/SOP.md格式与condition语法
  • SOP 如何运行
  • Gateway API:审批端点

【免费下载链接】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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询