範例
本頁將撰寫 Rules的全部概念套用至完整 rulesets。首先逐一說明 demo 中實際可執行的 ruleset files,再介紹 ruleset 擴大後常用的兩種 patterns,包括使用 Labels 保存 state,以及將 multi-signal Rule 分散整理於多個檔案。範例使用與其他文件相同的小型社群網站。
逐檔說明 demo ruleset
Demo 會執行 example_rules/ 中的實際 ruleset。內容只有兩個 Models、一個 Rule 與一個 Label,可以一次讀完。受治理平台有一項嚴格政策,使用者永遠不能說 hello。
Events 會以 JSON 傳入,包含 event name action_name、ID 與平台送出的資料。
{
"action_id": 1,
"action_name": "create_post",
"data": {
"user_id": "user_1923",
"event_type": "create_post",
"post": { "text": "hello world" }
}
}
main.sml 是 entrypoint。這個 ruleset 很小,不需要 conditional index.sml files,只要 import base Model,再 require 唯一的 Rule file。
Import(rules=['models/base.sml'])
Require(rule='rules/post_contains_hello.sml')
models/base.sml 定義每個 event 都有的 Features。UserId 與 EventType 會宣告為 Entities,讓 Osprey 把它們視為跨 Events 持續存在的對象,因此後續可以讓 Label 留在使用者上。預設為 true 的 coerce_type=True 會將 numeric user ID 等不符合宣告 Type 的值轉換成指定 Type,而非產生 error。
UserId: Entity[str] = EntityJson(
type='User',
path='$.user_id',
coerce_type=True
)
EventType: Entity[str] = EntityJson(
type='EventType',
path='$.event_type',
coerce_type=True
)
ActionName=GetActionName()
ActionId=GetActionId()
最後兩行從 Osprey 本身取得 event name 與 ID,不從 JSON payload 讀取。GetActionName() 與 GetActionId() 是 stdlib UDFs,將結果公開為 Features 後,就能在介面中查詢。
models/post.sml 加入只適用於貼文的 Feature。
PostText: Entity[str] = EntityJson(
type='PostText',
path='$.post.text',
coerce_type=True
)
rules/post_contains_hello.sml 會 import 兩個 Models、定義 Rule,再將 Rule 連接至 Effects,完整迴路都位於同一個檔案。
Import(
rules=[
'models/base.sml',
'models/post.sml',
]
)
ContainsHello = Rule(
when_all=[
EventType == 'create_post',
TextContains(text=PostText, phrase='hello')
],
description='Post contains the word "hello"',
)
WhenRules(
rules_any=[ContainsHello],
then=[
BanUser(entity=UserId, comment='User said "hello"'),
LabelAdd(entity=UserId, label='meow'),
],
)
TextContains 與 BanUser 不屬於 stdlib,而是 example_plugins/ 提供的 custom UDFs。自訂方式請見撰寫 UDFs。LabelAdd 屬於 stdlib。
config/labels.yaml 會宣告 Rule 套用的 Label,包括適用的 Entity types 與 connotation。
labels:
meow:
valid_for: [User]
connotation: positive
description: testing label
Events 開始流入後,每個處理完成的貼文都會出現在 Event Stream,其中擷取的 Features 包括 UserId、EventType、PostText 與 ContainsHello。查詢 ContainsHello == True 可以只顯示符合 Rule 的貼文。開啟符合條件貼文的作者後,可以看到 User Entity 上的 meow Label,以及記錄於 event 的 BanUser Effect。開始使用會使用即時 demo 資料逐步操作這些介面。
使用 Labels 保存 state
Rules 無法直接查看過去 Events,但 Labels 會跨 Events 保留在 Entities 上,因此某個 Rule 新增的 Label 可以成為另一個 Rule 後續檢查的條件。假設已經標示傳送過多 DMs 的使用者。
WhenRules(
rules_any=[
Sent_Too_Many_DMs,
],
then=[
LabelAdd(entity=UserId, label='likely_spammer')
],
)
此後,該使用者的每個 event 都會帶有這項 state,其他 Rules 可以在完全不同的 event type 上使用它。
Should_Warn_User_Of_Spammer = Rule(
when_all=[
HasLabel(entity=UserId, label='likely_spammer'),
This_Is_A_New_DM,
],
description=f'Likely spammer {UserId} started a new DM',
)
Labels 也會顯示在介面的 Entity 上,並可手動新增或移除。需要注意一項不對稱行為。HasLabel() 可以用於 Rules,但不能用於查詢列,因為查詢搜尋的是 Events,不是目前的 Entity state。若要尋找套用某個 Label 的 Events,改為查詢 DidAddLabel(entity_type="User", label_name="likely_spammer")。詳情請見查詢語法。
分散在多個檔案的 multi-signal Rule
下列完整流程使用撰寫 Rules 的 Rule 結構。目標是標示第一篇貼文同時 mention 至少一位使用者並包含 link 的帳號。三個 Signals 各自可能沒有問題,但同時出現時較為可疑。
撰寫 Rule
在 rules/record/post/first_post_link.sml 撰寫 Rule logic。檔案同時定義讓 Rule 得到 True 的條件,以及符合時採取的 actions。
# First, import the models that you will need inside of this rule
Import(
rules=[
'models/base.sml',
'models/record/post.sml',
],
)
# Next, define a variable that uses the `Rule` UDF
FirstPostLinkRule = Rule(
# Set the conditions in which this rule will be `True`
when_all=[
PostCount == 1, # if this is the user's first post
EmbedLink != None, # if there is a link inside of the post
ListLength(list=MentionIds) >= 1, # if there is at least one mention in the post
],
description='First post for user includes a link embed',
)
# Finally, set which effect UDFs will be triggered
WhenRules(
rules_any=[FirstPostLinkRule],
then=[
# This is a custom effect UDF that we have implemented
ReportRecord(
entity=PostId,
comment='This was the first post by a user and included a link',
severity=3,
),
],
)
連接 Rule
這個 Rule 只應在 event 是 post event 時執行。使用上述專案結構時會涉及三個檔案。
首先,project root 的 main.sml 使用單一 Require statement,指向 top-level Rules index。
Require(
rule='rules/index.sml',
)
接著,rules/index.sml 會在 event type 符合時,有條件地 require post Rules。
Import(
rules=[
'models/base.sml',
],
)
Require(
rule='rules/record/post/index.sml',
require_if=EventType == 'userPost',
)
最後,rules/record/post/index.sml require 新 Rule。
Import(
rules=[
'models/base.sml',
'models/record/post.sml',
],
)
Require(
rule='rules/record/post/first_post_link.sml',
)
可以在 demo ruleset 執行時修改它,例如為 TextContains 加入 phrase,或建立另一個針對 PostText 的 Rule。當所需 pattern 缺少 UDF 時,請前往 Integrations 與 Plugins。
治理提醒
Demo 中「出現
hello就停用使用者」是刻意簡化的測試政策。正式環境的 Rule 應使用具脈絡的 Signals、guardrails、分階段 rollout、誤判監測、人工複核與可回復 Effects。Labels 會跨 Events 影響未來判斷,應同時設計原因、到期、移除及稽核方式。