deny 或 sanitize——而你的代理以
"stream": true 呼叫閘道。真正重要的問題是:一個串流回應能在防火牆
決定之前洩漏一個被封鎖的工具呼叫嗎? 它不能,而本頁解釋讓那為真的
那一個機制,這樣你就能推理延遲與你客戶端收到的區塊。
這是對 SSE 行為的聚焦審視。關於裁決本身,參見
裁決;關於規則文法,參見
規則參考。
1. 串流防火牆 SSE 問題
一個非串流回應是一個 JSON 主體——防火牆看見整個東西、評估tool_calls,並傳回清理後的結果。一個串流則不同:一個模型會把
一個工具呼叫作為跨許多 SSE 框格的數十個 tool_call 差量發出,而
一旦一個框格被轉送,你的代理已經有了它——沒有撤回一個你已送出
的權杖這回事。評估太早,你就沒有完整的呼叫(名稱 + 完整引數)可
判定;邊走邊轉送,一個 deny 就已經太遲。
閘道以一個簡單、可觀察的合約解決這個問題:
內容即時串流
一般的文字與推理差量會不變地、即時地通過——對你使用者讀取的
權杖零附加延遲。
工具呼叫框格被保留
任何攜帶一個
tool_call(或舊版 function_call)差量的框格都會被
扣留在即時串流之外,直到該呼叫完整並被評估。防火牆是一個安全閘門,所以它解析每一個框格。 它不會從原始位元組
猜測一個框格是僅內容的——一個 JSON 跳脫過的
tool_calls 成員沒有
字面子字串可匹配,所以一個子字串捷徑會轉送一個未評估的工具呼叫。
SSE 框格很小;閘門解析每一個。2. 保留-組裝-評估序列
對一個 response 介面政策作用中的串流 chat-completions 回應而言,上游 發出的每一個框格走兩條路徑之一:內容 / role / 推理 / usage 框格 → 立即轉送
內容 / role / 推理 / usage 框格 → 立即轉送
立即逐位元組串流到你的客戶端。這些絕不攜帶一個工具呼叫,所以
防火牆沒有東西可決定。
tool_call(或舊版 function_call)框格 → 保留
tool_call(或舊版 function_call)框格 → 保留
被緩衝在即時串流之外。一個工具回合的結束
finish_reason 框格會
與它一起被保留,因為提早發出它會在防火牆裁定之前告訴你的客戶端
該回合已結束。arguments 片段)、在 response 介面上對照你的政策
評估每一個——與非串流路徑相同的裁決與規則語意——並只發出倖存者:
| 被保留呼叫的裁決 | 你的客戶端收到什麼 |
|---|---|
allow / audit | 原始的被保留框格,不變——一次延遲的通過,而非一個重新批次的區塊。 |
sanitize | 該呼叫,其引數被改寫(匹配到的密鑰/PII 被替換為一個有類型的權杖),重新發出。 |
deny | 該呼叫被丟棄。如果它是該回合唯一的呼叫,該回合以 finish_reason: "stop" 結束——串流看起來就像模型沒做任何工具呼叫。 |
3. 一個具體範例
一個帶有對*.delete 的 deny 規則的 response 政策(在主控台規則
編輯器中撰寫它)與一個其模型決定要同時呼叫 db.query 與 db.delete
的串流請求:
db.query——db.delete 被
組裝、評估、拒絕,而從未被發出。倖存的呼叫從 0 重新索引,而那個
被拒絕呼叫的防火牆事件會帶著觸發的規則落入你的
事件記錄。
4. Inbound 封鎖在串流開始前短路
被保留框格的這套機制只針對 response 介面——模型發出的呼叫。 一個inbound deny(一個代理公告的
工具)會在上游模型呼叫之前觸發,所以一個絆到 inbound 規則的串流
請求根本不會開啟一個 SSE 串流:它傳回一個普通的 HTTP 400、錯誤
代碼 firewall_blocked,標記為
skip-retry。
沒有框格、沒有被保留窗口——該封鎖就像任何非串流錯誤一樣落下。
5. 同一串流上的防護欄
一個串流回應能同時攜帶一個 防護欄輸出政策與 一個防火牆 response 政策。它們作用於不同的東西——防護欄審查模型 串流的文字;防火牆治理工具呼叫——而且它們組合:- 輸出防護欄封鎖(串流): 輸出掃描器在一條規則絆到的那一刻切斷
串流,轉送一個單一的通用替換區塊——
[Response blocked by content policy.],帶有finish_reason: "content_filter"——並停止。該訊息 刻意通用(沒有規則類別),這樣一個探測者無法列舉你的政策。當這 發生時一個在途中的防火牆保留會被丟棄,所以一個被扣留的工具呼叫 無法在封鎖後溜出去。 - 輸出防護欄遮罩(串流): 在模型即時之前遮罩請求;串流輸出的 即時帶內遮罩在路線圖上。在一個串流上,一條 mask 規則會記錄該匹配 但目前轉送原始區塊——撰寫它時要知道遮罩尚未在線路上被改寫。輸出 block 在串流上被完整強制執行。
本頁描述 OpenAI chat-completions 的 SSE 形狀。 同一個保留-評估-發出
合約是按格式接線的——原生 Anthropic Messages、Gemini、xAI,以及
OpenAI Responses 串流各自以它們自己的事件形狀攜帶它——所以無論哪個
供應商服務了該請求,客戶可觀察的行為都相同。
6. 這對你的客戶端意味著什麼
被保留框格模型的幾個實際後果:finish_reason 可能改變
finish_reason 可能改變
一個其唯一工具呼叫被拒絕的回合,會以
finish_reason: "stop" 而非
"tool_calls" 結束——對你的代理而言它讀起來像「模型選擇不呼叫
一個工具」。一個有某些呼叫倖存的回合會以 "tool_calls" 結束,
只攜帶倖存者。usage 仍會抵達
usage 仍會抵達
當一個上游把權杖
usage 捆綁在防火牆保留的同一個終端區塊上時,
閘道會把它重新附加到最終重建的框格——一個請求了串流 usage 的
客戶端仍會得到它。共享一個工具呼叫區塊的文字會被保留
共享一個工具呼叫區塊的文字會被保留
如果模型在同一個框格中發出了內容與一個工具呼叫,即使該工具
呼叫被剝除,內容也會被復原並重新發出——封鎖一個呼叫絕不會丟掉
你的助理文字。
無代理程式碼變更
無代理程式碼變更
你不需要讓一個串流選擇加入這其中任何一項。將一個政策綁定到金鑰
(或設定一個工作區預設值)並像以前一樣繼續串流——強制執行在
閘道處。
接下來去哪裡
介面與介面層
inbound、response、mcp、egress——每條規則在哪裡評估。
裁決
allow、audit、deny、sanitize、pending_approval、cap_cost。
淨化引數
從一個工具呼叫的引數中遮罩密鑰——僅限引數層。
影子模式
在你衡量影響時將強制執行的裁決降級為 audit。
