Community程式設計與開發github.com

fangdididi/trigger-rehearsal

在啟用背景或事件驅動型智能代理的觸發規則之前,先進行排練。將各種格式的規則(代理中樞規則檔、常規流程、CI「on:」區塊、cron 行、無程式碼自動化)轉換成一份中立的規則表,並檢查是否有自我觸發迴圈、未受保護的鏈結、遺漏的上限和重複處理,然後重播真實或記錄的事件,以顯示每個會啟動的運行、合併、跳過、排隊或丟棄的項目,以及每次運行的成本。

trigger-rehearsal 是什麼?

`trigger-rehearsal` 是一個關於 AI 代理技能的工具,旨在安全地模擬與驗證觸發規則。在實際啟用背景或事件驅動型代理之前,它會將來自不同平台(如代理中樞、CI 工作流、cron 排程或無程式碼自動化)的規則轉換為統一的 JSON 格式(`rules.json`),並對其進行靜態檢查(lint),以找出自我觸發、未封閉的迴圈、遺漏的限制或重複處理等潛在問題。接著,它會重播來自真實日誌或合成事件的數據,生成詳細的時間軸報告,顯示每次觸發會如何執行:哪些運行會啟動、合併、跳過、排隊或丟棄,以及相關的成本。此工具特別適合用於 GitHub Actions、cron 排程器或自訂事件驅動系統的開發者,幫助他們在部署前以數據驅動的方式最佳化並保護其自動化規則,避免意外消耗資源或造成無限迴圈。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/fangdididi/trigger-rehearsal/tree/HEAD

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

觸發規則排練

這是什麼

一個根據事件自行喚醒的代理,其安全程度完全取決於它的觸發規則。即使代理本身設計得很謹慎,仍可能在一天內燒光預算,因為問題通常出在上一層:

  • 一條規則對自身工作所產生的事件做出反應,導致它不斷重複觸發;
  • 兩條規則透過第二個系統互相餵養(代理發表評論,評論被同步,同步觸發 webhook);
  • 一個嘈雜的來源在兩分鐘內發送了十次編輯,每一次都啟動一個全新的運行;
  • 一條 cron 行原本只是「偶爾檢查一下」,卻每天觸發 288 次;
  • 兩條規則處理了同一個事件,導致工作被重複執行;
  • 一條規則執行了退款、刪除或向用戶發送訊息等操作,卻未先徵求同意。

當你只用一個範例測試代理時,這些問題都不會顯現。它們只有在真實流量到來時才會浮出檯面。觸發規則排練讓真實流量提前在紙面上發生:你將規則以一種中立格式記錄下來,重播一天或一週的事件,然後在任何規則被啟用之前,先行檢視事件時間軸。

使用時機

  • 在啟用任何允許代理在無人輸入提示的情況下開始工作的規則之前。
  • 在向現有規則集合新增規則時(新規則可能會關閉一個原本開放的迴圈)。
  • 當一個背景代理的運行次數遠高於或遠低於預期時。
  • 當需要為去抖視窗、並行限制和每日上限選擇數值,而且你想要的是數據而非猜測時。
  • 當審查者問「如果這個儲存庫在一小時內收到 50 個 issue 會怎樣?」時。

不適用於:檢查代理在運行內部做了什麼(這是提示詞或技能的問題),或對事件來源本身進行負載測試。

規則表

所有規則都會被轉換成 rules.json。保持轉換的真實性:一條規則表對應一條真實規則,並寫下實際存在的防護措施,而不是你打算未來才加入的那些。

{
  "tz": "Asia/Shanghai",
  "run_s": 180,
  "external_sources": ["github"],
  "budget": {"runs_per_day": 40, "cost_per_run": 0.15, "cost_per_day": 6},
  "rules": [
    {
      "name": "triage-new-issue",
      "on": {"source": "github", "types": ["issues.opened", "issues.edited"]},
      "where": {"repo": "me/app"},
      "key": "subject",
      "debounce_s": 300,
      "parallel": 1,
      "max_per_day": 20,
      "unless_actor": ["my-bot"],
      "unless_label": "triaged",
      "emits": [{"source": "github", "types": ["issues.labeled"], "actor": "my-bot", "labels": ["triaged"]}],
      "sensitive": [],
      "asks": false
    },
    {"name": "morning-digest", "on": {"cron": "0 9 * * 1-5"}, "max_per_day": 1}
  ]
}
欄位意義
on.source + on.types哪些事件會啟動規則。若省略 types,則會比對該來源的所有類型。
on.cron一個五欄位的 cron 行,時區為規則表的 tz。一條規則只能有 source 或 cron 其中之一。
where事件欄位必須等於某個值(或屬於某個列表)。
key用於命名主題(如 issue 編號、工單 ID、URL)的事件欄位。用於合併重複事件。
debounce_s在同一個主題上等待最後一個事件這麼久之後,才執行一次。
parallel此規則最多可以同時有多少個運行在進行。額外的運行會排隊等待。
max_per_day每個本地日期的硬性上限。超過上限的運行會被丟棄。
run_s典型的運行長度,用於排隊。若未指定,則回退到規則表頂層的值,再回退到 120 秒。
unless_actor, unless_label防護措施:跳過由這些參與者產生或帶有此標籤的事件。
emits運行本身產生的輸出事件(例如評論、標籤、狀態變更、後續事件)。需包含參與者及其將攜帶的標籤。
sensitive + asks需要真人介入的動作(例如刪除、花費、重啟、發送)。asks: true 表示運行會停止並先詢問。
external_sources由外部世界餵養的來源。除此之外的任何來源都必須由某條規則產生。
budget可選的每日運行次數、每次運行成本及每日成本,用於最終判決。

逐步操作指南

  1. 收集真實規則。 閱讀實際的設定檔,而不是對它的描述。代理中樞規則檔、已儲存的常規流程、CI 工作流的 on: 區塊、crontab 行、無程式碼自動化畫面。注意任何會回寫到某個規則監視的來源的操作。
  2. 轉換為 rules.json。 從 python3 scripts/rehearse.py new rehearsal/ 開始。對於每條規則,詢問三個問題並填寫欄位:是什麼喚醒它?一次運行會回寫什麼?是什麼阻止它被自己的輸出喚醒?如果第三個問題的答案是「沒有」,就將防護措施留空。排練的目的就是要抓住這個問題。
  3. 靜態檢查。 python3 scripts/rehearse.py lint rehearsal/rules.json。在重播之前修復所有 HOLD 問題;REVIEW 問題可以等看到數字後再處理。
  4. 收集事件。 最佳做法:從來源(如 webhook 傳遞日誌、issue 時間軸、客服稽核日誌)匯出一個真實的忙碌日,形成 events.jsonl 檔案,每行一個 JSON 物件,至少包含 t、source、type,以及你的 key 和 where 會用到的欄位。如果沒有歷史記錄,則故意撰寫一段簡短的合成爆發事件:三分鐘內十次編輯、同時有兩個主題、一個由你自己的機器人產生的事件。
  5. 重播整個天數。 python3 scripts/rehearse.py replay rehearsal/rules.json rehearsal/events.jsonl --from 2026-11-02T00:00:00Z --to 2026-11-03T00:00:00Z --md rehearsal/report.md。傳入 --from/--to 參數,這樣每日的數字才不會因為短時間視窗而被誇大。
  6. 閱讀時間軸,而不僅僅是判決。 尋找在幾分鐘內對同一個主題的重複運行(缺少或去抖時間過短)、深度不斷增加的鏈結(迴圈)、長時間等待(並行度太低)、丟棄事件(上限太低,無法應付實際工作,或表示規則對雜訊過度反應)。
  7. 調整並重複。 一次只改變一個參數,並重播相同的事件,這樣才能清楚看出變化帶來的差異。
  8. 做出決定。 只有在最後一次重播在一個真實的日期上得到 ARM 判決時,才啟用規則。保留最終的 rules.json、事件檔案和 report.md,作為啟用規則的依據記錄。

判決

  • ARM:沒有 HOLD 或 REVIEW 的發現。按照規則內容啟用是安全的。
  • TRIM:有某些問題會浪費運行次數或導致工作被跳過(缺少 key、無上限、規則重疊、上限導致真實事件被丟棄、接近預算)。只有在調整後,或附上書面理由時,才能啟用。
  • HOLD:不要啟用。存在自我迴圈、未受保護的循環、深度超過 5 的鏈結、未經詢問的敏感操作、超出預算,或是損壞的 cron 行。

退出碼會對應判決結果(0、1、2;3 表示輸入錯誤),因此排練可以在 CI 中作為設定變更的閘門。

範例

一個客服團隊希望用代理回答工單。他們的規則表(位於 examples/loopy/)中有一個回答規則,該規則會張貼回覆,而回覆會更新工單,這又會符合同一條規則的觸發條件。對一個普通工作日進行重播,結果如下:

HOLD   self-loop            answer-ticket: rule's own output matches its own trigger with no unless_actor/unless_label guard
HOLD   runaway              answer-ticket: chain kept re-firing past depth 5 (3 cuts)
HOLD   sensitive-unasked    refund-check: declares sensitive actions ['refund'] but 'asks' is not true
HOLD   over-runs-budget     -: 100.0/day against a budget of 30/day
REVIEW cron-tight           inbox-poll: up to 720 fires/day
VERDICT HOLD

三張工單的更新變成了 18 次回答運行。修復方法很簡單:在回答規則中加入 "unless_actor": ["support-bot"],將退款規則設定為 "asks": true,並將每兩分鐘的輪詢替換成一個僅在收件匣真正有變動時才發出 inbox.changed 事件的腳本(請參閱 examples/loopy/rules.fixed.json)。對同一天進行下一次重播,運行次數從 100 次下降到 4 次,判決也變成了 TRIM。剩下的一個發現是,回答規則和退款檢查規則都會看到退款工單,但在這個案例中這是預期的行為,因此在報告旁附上一行書面理由後予以接受。

小技巧

  • 最好的單一迴圈防護措施是一個在運行後會變成 false 的條件:例如運行會新增一個規則排除的標籤,或是一個規則不再符合的狀態。
  • 跨系統的迴圈最容易遺漏。如果代理寫入系統 A,而系統 A 同步到系統 B,請將該同步事件以 emits 列出,並註明它會顯示的參與者。
  • 去抖預設會引入延遲。請根據一個人完成一系列編輯所需的時間來選擇視窗大小,並在有人等待回覆時保持較短的視窗。
  • 去抖不是動作的去重。對於任何不可逆的操作,運行本身仍應檢查「是否已完成?」後再執行。
  • 每日上限是保險絲,而不是規劃。如果一個普通日的重播會觸發上限,表示規則範圍太廣。
  • 每隔幾分鐘輪詢一次,通常可以改用一個小型腳本來比較狀態並在變動時發出一個事件;這樣代理只會在有事發生時才被喚醒,成本更低。
  • 每當新增一條規則時,就重新執行排練。大多數迴圈是由第二條規則創造的,而不是第一條。

相關技能