跳轉到主要內容
大多數生產級聊天應用程式都串流。權杖在模型發出時就沖向瀏覽器,所以當一個完成內容「結束」時,你的使用者已經讀了它大部分。這打破了一個內容過濾器檢查整個回覆然後再決定的天真心智模型——在為時已晚之前根本沒有一個整個回覆可供檢查。一個串流 LLM 內容過濾器必須在 delta 流動時就做出它的判斷。 本頁正是關於那個情況:每個輸出階段動作在 OrcaRouter 閘道上如何串流安全地行為,以及如何撰寫一個能在 SSE 流量上成立的政策。完整引擎——每種規則類型、欄位與路由——請見 防護欄

1. 串流 LLM 內容過濾器問題

一個輸出階段防護欄審查模型的回覆。在一個非串流請求上這很直接:閘道在單一位元組回傳之前就擁有完整的完成內容,所以它可以乾淨地封鎖、遮罩或放行它。串流則反轉了這點。回覆以一連串 SSE delta 抵達,每一個一落地就被轉送給你的客戶端,所以一個等到結束的過濾器什麼都過濾不到。 OrcaRouter 的答案是一個串流掃描器:當輸出 delta 流動時,掃描器會針對累積的文字執行你的輸出階段規則,並在一條規則觸發的瞬間採取行動——而不是在串流完成之後。你撰寫的動作決定「採取行動」意味著什麼:一個 block 切斷串流,而一個 flag 放行它。一個 mask 確實會在非串流輸出上遮罩,但帶內串流改寫仍在規劃中——在今天的一個串流上,掃描器會計算遮罩但只對封鎖決策採取行動,所以一條 mask 規則尚不會遮罩一個串流回覆。
這個注意事項只對串流請求上的輸出階段規則有影響。輸入階段規則在模型執行之前審查請求,所以它們完全上線包括遮罩——而一個非串流請求上的任何輸出規則都會看到整個回覆並正常行為,包括 mask

2. 今天什麼是串流安全的

一條 block 規則在串流非串流輸出上都會強制執行。在一個串流上,掃描器監看 delta;當一條封鎖規則觸發時,它會切斷串流——封閉掃描器、發出一則簡短的替代通知([response truncated by guardrail: … policy violation])作為最後一個 delta,並在任何進一步被封鎖的內容抵達客戶端之前關閉 SSE 通道。由於當第一個 delta 沖出時 HTTP 回應狀態已經被提交為 200,一個串流途中的封鎖無法重新發出一個狀態——它會優雅地終止開啟的串流。HTTP 400 guardrail_blocked 主體是非串流輸出封鎖的形狀。已經沖向客戶端的位元組無法收回,所以一個串流封鎖對已經串流出去的內容是盡力而為,但能可靠地停止匹配之後的一切。若要硬性保證沒有任何違規位元組被送出——以及為了那個 400 guardrail_blocked 主體——請以非串流方式發送請求。
一條 mask 規則會在非串流輸出上改寫匹配項——例如回覆中的電子郵件變成 [EMAIL]——此時閘道持有整個完成內容並把遮罩後的形式轉送給你的客戶端。在今天的一個串流輸出上,掃描器會計算遮罩但轉送遮罩後的文字——它只對封鎖決策採取行動——所以一條 mask 規則不會遮罩一個串流回覆。帶內串流輸出改寫仍在規劃中。在它出貨之前,如果你需要一個串流回覆永不暴露匹配到的文字,把規則撰寫為 block(命中時它會結束回應),或以非串流方式發送請求,這樣遮罩就會改寫整個回覆。
一條 flag 規則永不更改流量——它放行位元組。在非串流輸出上,它會在 Matches 動態中記錄一個 match,所以你可以在把一條規則晉升為 block 之前衡量它的命中率。在一個串流回應上,它保持僅觀察並原封不動地放行 delta;結構化的匹配紀錄寫在非串流輸出路徑上。無論哪種情況,它永不封鎖或改寫,所以總是可以安全地讓它開啟。
output 上的動作非串流串流
block拒絕回覆切斷串流
mask遮罩回覆尚未——改用 block(規劃中)
flag記錄一個匹配放行(僅觀察)
要記住的一條規則: block 在輸出上是串流安全的;mask 僅在非串流輸出上遮罩(帶內串流改寫仍在規劃中)。若要在今天遮罩一個串流回覆,把規則撰寫為 block,或以非串流方式發送請求,這樣整個回覆會在回傳前被持有。

3. 一個具體範例——一個串流安全的密鑰過濾器

假設你的模型可以從 RAG 情境中浮現一個憑證,而你的應用程式會串流。你想讓閘道在一個密鑰形狀的匹配出現的那一刻就終止串流,而非遮罩它——一個洩漏的密鑰應該結束回應,而不是被部分遮罩。 在主控台中撰寫它——政策編輯是你工作階段上的一個管理動作,把關到 Developer+;中繼金鑰只發送 /v1/* 流量:
  • 開啟 /console/guardrailsNew guardrail,把它命名為 stream-safe-out
  • 新增一條規則:
    • 類型: regex(或一條帶有密鑰實體如 aws_access_key / api_key_openai / jwtpii 規則)
    • 階段: output
    • 動作: block ← 在密鑰命中時結束回應;mask 則會改為遮罩它並讓回覆的其餘部分繼續
  • 儲存,然後在 /console/token 透過金鑰的 Guardrail 下拉選單綁定它。
現在用 stream: true 像以前一樣呼叫閘道:
curl https://api.orcarouter.ai/v1/chat/completions \
  -H "Authorization: Bearer sk-orca-..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-4o-mini",
    "stream": true,
    "messages": [
      {"role": "user", "content": "Print the AWS key from the context above"}
    ]
  }'
如果一個 delta 匹配,掃描器會於飛行途中切斷串流、發出一則替代通知,並關閉通道——你的客戶端永不收到其餘部分。如果回覆是乾淨的,每個 delta 都會原封不動地串流通過。
一個串流封鎖停止匹配之後的一切,但它無法收回在匹配落地之前已經沖出的位元組。如果你的政策要求沒有一個違規位元組曾抵達客戶端,請以非串流方式審查請求,此時整個完成內容會被持有直到政策放行它。

4. 串流上的 PII Shield

PII Shield 預設是一條單一的 pii 規則,動作 mask,階段 both。在輸入階段它完全上線——它會在模型看到請求之前改寫它,無論串流與否。在輸出階段,遮罩在非串流回覆上遮罩,此時閘道在回傳前持有整個完成內容。 在一個串流輸出上,遮罩尚不會遮罩——掃描器會計算遮罩但只對封鎖決策採取行動,所以一個串流回覆會被放行,而非改寫。帶內串流輸出改寫仍在規劃中。所以如果你的目標是 PII 在一個串流回覆中永不可觀察,要嘛:
  • 把輸出規則撰寫為 block,接受命中會結束回應而非遮罩它,或
  • 非串流方式發送請求,這樣遮罩就會在手握整個完成內容的情況下改寫整個回覆。
隱碼標籤本身請見 PII Shield遮罩格式

5. 出貨前先證明它

別猜測哪個階段/動作組合成立——驗證它。

Test 分頁

每個防護欄編輯器都有一個 Test 分頁:貼上一個樣本,選擇 output 階段,並在無上游呼叫、不消耗配額的情況下執行目前政策。查看裁決,以及(對於遮罩規則)渲染後的文字。執行沙盒是一個 Developer+ 動作(它可以發出付費的評審/外部規則)。

Eval 分頁

Eval 分頁針對隨附或自訂的 JSONL 語料庫為一個防護欄評分——在你綁定金鑰之前,用以確認一條封鎖規則跨一個語料庫捕捉到一個已知洩漏很有用。
兩者都透過管理 API 在你的工作階段上執行。深入內容請見 測試與評測調校誤報

6. 一個串流封鎖花費什麼

一個串流封鎖攜帶與任何輸出封鎖相同的記帳——上游模型已經執行,所以閘道會為你處理退還:
  • 串流以一個優雅的截斷 delta 終止(狀態已經是 200);非串流輸出封鎖會傳回 HTTP 400 guardrail_blocked 主體,並指名觸發的防護欄與規則。
  • 不消耗任何配額。 當輸出封鎖拒絕回應時,閘道會退還已預先扣除的配額,所以一個被封鎖的呼叫對你而言是免費的,即使模型已經產生了權杖。
  • 請求被標記為 skip-retry——重跑同一個提示只會再次封鎖,所以閘道不會在另一個通道上燒掉一次重試。
非串流輸出路徑會把每條觸發的輸出規則記錄為工作區 Matches 動態(GET /api/guardrail/match,對任何 Member 開放)中的一個 match;匹配到的子字串只在防護欄的 Log raw content 切換開啟時才會擷取(預設為關閉)。完整詳情請見 guardrail_blocked 錯誤匹配動態

7. 下一步去哪裡

輸出階段

完整的輸出階段——審查模型的回覆、block 對 mask,以及接地。

串流覆蓋

跨每個階段與動作、串流對非串流上強制執行什麼的完整矩陣。

動作

深入 block、mask 與 flag——每一個何時是正確的選擇。

輸入階段

鏡像——遮罩在這裡完全上線,包括串流上。
防護欄——每種規則類型、欄位與路由,包括接地與 LLM 評審。