1. 串流 LLM 內容過濾器問題
一個輸出階段防護欄審查模型的回覆。在一個非串流請求上這很直接:閘道在單一位元組回傳之前就擁有完整的完成內容,所以它可以乾淨地封鎖、遮罩或放行它。串流則反轉了這點。回覆以一連串 SSE delta 抵達,每一個一落地就被轉送給你的客戶端,所以一個等到結束的過濾器什麼都過濾不到。 OrcaRouter 的答案是一個串流掃描器:當輸出 delta 流動時,掃描器會針對累積的文字執行你的輸出階段規則,並在一條規則觸發的瞬間採取行動——而不是在串流完成之後。你撰寫的動作決定「採取行動」意味著什麼:一個block 切斷串流,而一個 flag 放行它。一個 mask 確實會在非串流輸出上遮罩,但帶內串流改寫仍在規劃中——在今天的一個串流上,掃描器會計算遮罩但只對封鎖決策採取行動,所以一條 mask 規則尚不會遮罩一個串流回覆。
這個注意事項只對串流請求上的輸出階段規則有影響。輸入階段規則在模型執行之前審查請求,所以它們完全上線包括遮罩——而一個非串流請求上的任何輸出規則都會看到整個回覆並正常行為,包括
mask。2. 今天什麼是串流安全的
block——串流安全(於飛行途中切斷串流)
block——串流安全(於飛行途中切斷串流)
一條
block 規則在串流與非串流輸出上都會強制執行。在一個串流上,掃描器監看 delta;當一條封鎖規則觸發時,它會切斷串流——封閉掃描器、發出一則簡短的替代通知([response truncated by guardrail: … policy violation])作為最後一個 delta,並在任何進一步被封鎖的內容抵達客戶端之前關閉 SSE 通道。由於當第一個 delta 沖出時 HTTP 回應狀態已經被提交為 200,一個串流途中的封鎖無法重新發出一個狀態——它會優雅地終止開啟的串流。HTTP 400 guardrail_blocked 主體是非串流輸出封鎖的形狀。已經沖向客戶端的位元組無法收回,所以一個串流封鎖對已經串流出去的內容是盡力而為,但能可靠地停止匹配之後的一切。若要硬性保證沒有任何違規位元組被送出——以及為了那個 400 guardrail_blocked 主體——請以非串流方式發送請求。mask——僅非串流輸出(帶內串流改寫仍在規劃中)
mask——僅非串流輸出(帶內串流改寫仍在規劃中)
一條
mask 規則會在非串流輸出上改寫匹配項——例如回覆中的電子郵件變成 [EMAIL]——此時閘道持有整個完成內容並把遮罩後的形式轉送給你的客戶端。在今天的一個串流輸出上,掃描器會計算遮罩但不轉送遮罩後的文字——它只對封鎖決策採取行動——所以一條 mask 規則不會遮罩一個串流回覆。帶內串流輸出改寫仍在規劃中。在它出貨之前,如果你需要一個串流回覆永不暴露匹配到的文字,把規則撰寫為 block(命中時它會結束回應),或以非串流方式發送請求,這樣遮罩就會改寫整個回覆。flag——僅觀察,永不更改流量
flag——僅觀察,永不更改流量
一條
flag 規則永不更改流量——它放行位元組。在非串流輸出上,它會在 Matches 動態中記錄一個 match,所以你可以在把一條規則晉升為 block 之前衡量它的命中率。在一個串流回應上,它保持僅觀察並原封不動地放行 delta;結構化的匹配紀錄寫在非串流輸出路徑上。無論哪種情況,它永不封鎖或改寫,所以總是可以安全地讓它開啟。在 output 上的動作 | 非串流 | 串流 |
|---|---|---|
block | 拒絕回覆 | 切斷串流 |
mask | 遮罩回覆 | 尚未——改用 block(規劃中) |
flag | 記錄一個匹配 | 放行(僅觀察) |
3. 一個具體範例——一個串流安全的密鑰過濾器
假設你的模型可以從 RAG 情境中浮現一個憑證,而你的應用程式會串流。你想讓閘道在一個密鑰形狀的匹配出現的那一刻就終止串流,而非遮罩它——一個洩漏的密鑰應該結束回應,而不是被部分遮罩。 在主控台中撰寫它——政策編輯是你工作階段上的一個管理動作,把關到 Developer+;中繼金鑰只發送/v1/* 流量:
- 開啟
/console/guardrails,New guardrail,把它命名為stream-safe-out。 - 新增一條規則:
- 類型:
regex(或一條帶有密鑰實體如aws_access_key/api_key_openai/jwt的pii規則) - 階段:
output - 動作:
block← 在密鑰命中時結束回應;mask則會改為遮罩它並讓回覆的其餘部分繼續
- 類型:
- 儲存,然後在
/console/token透過金鑰的 Guardrail 下拉選單綁定它。
stream: true 像以前一樣呼叫閘道:
4. 串流上的 PII Shield
PII Shield 預設是一條單一的pii 規則,動作 mask,階段 both。在輸入階段它完全上線——它會在模型看到請求之前改寫它,無論串流與否。在輸出階段,遮罩在非串流回覆上遮罩,此時閘道在回傳前持有整個完成內容。
在一個串流輸出上,遮罩尚不會遮罩——掃描器會計算遮罩但只對封鎖決策採取行動,所以一個串流回覆會被放行,而非改寫。帶內串流輸出改寫仍在規劃中。所以如果你的目標是 PII 在一個串流回覆中永不可觀察,要嘛:
- 把輸出規則撰寫為 block,接受命中會結束回應而非遮罩它,或
- 以非串流方式發送請求,這樣遮罩就會在手握整個完成內容的情況下改寫整個回覆。
5. 出貨前先證明它
別猜測哪個階段/動作組合成立——驗證它。Test 分頁
每個防護欄編輯器都有一個 Test 分頁:貼上一個樣本,選擇
output 階段,並在無上游呼叫、不消耗配額的情況下執行目前政策。查看裁決,以及(對於遮罩規則)渲染後的文字。執行沙盒是一個 Developer+ 動作(它可以發出付費的評審/外部規則)。Eval 分頁
Eval 分頁針對隨附或自訂的 JSONL 語料庫為一個防護欄評分——在你綁定金鑰之前,用以確認一條封鎖規則跨一個語料庫捕捉到一個已知洩漏很有用。
6. 一個串流封鎖花費什麼
一個串流封鎖攜帶與任何輸出封鎖相同的記帳——上游模型已經執行,所以閘道會為你處理退還:- 串流以一個優雅的截斷 delta 終止(狀態已經是 200);非串流輸出封鎖會傳回 HTTP 400
guardrail_blocked主體,並指名觸發的防護欄與規則。 - 不消耗任何配額。 當輸出封鎖拒絕回應時,閘道會退還已預先扣除的配額,所以一個被封鎖的呼叫對你而言是免費的,即使模型已經產生了權杖。
- 請求被標記為 skip-retry——重跑同一個提示只會再次封鎖,所以閘道不會在另一個通道上燒掉一次重試。
GET /api/guardrail/match,對任何 Member 開放)中的一個 match;匹配到的子字串只在防護欄的 Log raw content 切換開啟時才會擷取(預設為關閉)。完整詳情請見 guardrail_blocked 錯誤 和 匹配動態。
7. 下一步去哪裡
輸出階段
完整的輸出階段——審查模型的回覆、block 對 mask,以及接地。
串流覆蓋
跨每個階段與動作、串流對非串流上強制執行什麼的完整矩陣。
動作
深入 block、mask 與 flag——每一個何時是正確的選擇。
輸入階段
鏡像——遮罩在這裡完全上線,包括串流上。
