메인 콘텐츠로 건너뛰기
response 표면 규칙 — 모델이 발행하는 툴 호출에 대한 denysanitize — 을 활성화했고, 에이전트가 "stream": true로 게이트웨이를 호출합니다. 실제로 중요한 질문: 스트리밍 응답이 firewall이 결정하기 전에 차단된 툴 호출을 누출할 수 있는가? 그럴 수 없으며, 이 페이지는 그것을 참으로 만드는 한 메커니즘을 설명하여 지연과 클라이언트가 받는 청크에 대해 추론할 수 있게 합니다. 이것은 SSE 동작에 대한 집중된 관찰입니다. 판정 자체는 Verdicts를; 규칙 문법은 규칙 레퍼런스를 참조하세요.

1. 스트리밍 firewall SSE 문제

비스트리밍 응답은 하나의 JSON 본문입니다 — firewall이 전체를 보고, tool_calls를 평가하고, 정화된 결과를 반환합니다. 스트림은 다릅니다: 모델은 툴 호출을 많은 SSE 프레임에 걸쳐 수십 개의 tool_call 델타로 발행하며, 프레임이 일단 전달되면, 에이전트가 이미 그것을 가지고 있습니다 — 보낸 토큰을 철회할 수는 없습니다. 너무 일찍 평가하면 판단할 완전한 호출(이름 + 전체 인자)이 없고; 진행하면서 전달하면 deny는 이미 너무 늦습니다. 게이트웨이는 이것을 간단하고 관측 가능한 계약으로 해결합니다:

콘텐츠는 라이브로 스트리밍

일반 텍스트와 추론 델타는 변경 없이, 실시간으로 통과합니다 — 사용자가 읽는 토큰에 추가 지연 제로.

툴 호출 프레임은 보류

tool_call(또는 레거시 function_call) 델타를 담은 모든 프레임은 호출이 완료되고 평가될 때까지 라이브 스트림에서 보류됩니다.
firewall은 보안 게이트이므로, 모든 프레임을 파싱합니다. 원시 바이트에서 프레임이 콘텐츠 전용이라고 추측하지 않습니다 — JSON 이스케이프된 tool_calls 멤버는 매치할 리터럴 부분 문자열이 없으므로, 부분 문자열 지름길은 평가되지 않은 툴 호출을 전달하게 됩니다. SSE 프레임은 작습니다; 게이트는 각각을 파싱합니다.

2. 보류-조립-평가 시퀀스

response 표면 정책이 활성인 스트리밍 chat-completions 응답에 대해, 업스트림이 발행하는 각 프레임은 두 경로 중 하나를 취합니다:
바이트 단위로 즉시 클라이언트로 스트리밍됩니다. 이것들은 결코 툴 호출을 담지 않으므로, firewall이 결정할 것이 없습니다.
라이브 스트림 밖으로 버퍼됩니다. 툴 턴의 닫는 finish_reason 프레임은 그것과 함께 보류됩니다. 그것을 일찍 발행하면 firewall이 판정하기 전에 클라이언트에게 턴이 끝났다고 알리게 되기 때문입니다.
스트림 끝에, 게이트웨이는 보류된 프레임을 완전한 툴 호출로 조립하고(각 호출의 스트리밍된 arguments 조각을 결합), response 표면에서 정책에 대해 모두 평가하고 — 비스트리밍 경로와 동일한 판정 및 규칙 의미 — 살아남은 것만 발행합니다:
보류된 호출의 판정클라이언트가 받는 것
allow / audit변경 없는 원래 보류된 프레임 — 다시 배치된 청크가 아니라 지연된 그대로 전달.
sanitize인자가 다시 쓰인(매치된 시크릿/PII가 타입 지정 토큰으로 교체된) 호출, 재발행됨.
deny호출이 드롭됨. 그것이 턴의 유일한 호출이었으면, 턴은 finish_reason: "stop"으로 닫힙니다 — 스트림은 모델이 툴 호출을 하지 않은 것처럼 보입니다.
아무것도 매치하지 않으면, 툴 호출 프레임의 버퍼링 지연만 지불합니다 — 콘텐츠는 이미 라이브로 스트리밍됨. firewall은 실제로 행동할 때만(deny나 sanitize) 프레임을 재구성합니다; 깨끗한 allow은 업스트림의 정확한 바이트를 전달합니다.

3. 하나의 구체적인 예

*.delete에 대한 deny 규칙(콘솔 규칙 편집기에서 작성)을 가진 response 정책과, 모델이 db.querydb.delete을 둘 다 호출하기로 결정하는 스트리밍 요청:
SSE timeline (what your agent receives)
───────────────────────────────────────
data: {"choices":[{"delta":{"content":"Looking that up…"}}]}   ← live
data: {"choices":[{"delta":{"content":" one moment."}}]}        ← live
                                                                ← db.query + db.delete
                                                                  tool_call frames HELD
─── end of stream ───
data: {"choices":[{"delta":{"role":"assistant",
        "tool_calls":[{"index":0,"function":{"name":"db.query",…}}]}}]}
data: {"choices":[{"finish_reason":"tool_calls"}]}
에이전트는 어시스턴트 텍스트를 실시간으로 읽은 다음, db.query 받습니다 — db.delete은 조립되고, 평가되고, 거부되고, 결코 발행되지 않았습니다. 살아남은 호출은 0부터 재인덱싱되고, 거부된 호출에 대한 firewall 이벤트는 발동한 규칙과 함께 이벤트 로그에 떨어집니다.
스트리밍 response 정책은 먼저 shadow mode하에서 롤아웃하세요. shadow mode에서 모든 강제 판정이 audit로 강등되고(이유에 [shadow] would … 접두) 모든 툴 호출 프레임이 통과합니다 — 그래서 정책이 호출을 드롭하기 시작하기 전에 실제 스트리밍 트래픽에서 예상한 것에 매치하는지 확인할 수 있습니다.

4. inbound block은 스트림이 시작되기 전에 단락됩니다

보류 프레임 춤은 response 표면 — 모델이 발행하는 호출 — 만을 위한 것입니다. inbound deny(에이전트가 광고하는 툴)는 업스트림 모델 호출 전에 발동하므로, inbound 규칙에 걸리는 스트리밍 요청은 SSE 스트림을 아예 열지 않습니다: 오류 코드 firewall_blocked와 함께 평범한 HTTP 400을 반환하며, skip-retry로 표시됩니다. 프레임 없음, 보류 윈도우 없음 — block은 다른 어떤 비스트리밍 오류처럼 떨어집니다.

5. 같은 스트림의 Guardrails

스트리밍 응답은 Guardrail 출력 정책 firewall response 정책을 동시에 담을 수 있습니다. 그것들은 서로 다른 것에 작용하며 — guardrails는 모델이 스트리밍하는 텍스트를 스크리닝하고; firewall은 툴 호출을 통제 — 조합됩니다:
  • 출력 guardrail block (스트리밍): 출력 스캐너는 규칙이 걸리는 순간 스트림을 끊고, 단일 일반 교체 청크 — finish_reason: "content_filter"를 가진 [Response blocked by content policy.] — 를 전달하고, 멈춥니다. 메시지는 의도적으로 일반적입니다(규칙 카테고리 없음). 그래야 탐색자가 정책을 열거할 수 없습니다. 이때 진행 중인 firewall 보류는 폐기되므로, 보류된 툴 호출이 block 후 빠져나갈 수 없습니다.
  • 출력 guardrail mask (스트리밍): 요청을 모델이 라이브가 되기 전에 마스킹; 스트리밍 출력의 라이브 인밴드 마스킹은 로드맵에 있습니다. 스트림에서 mask 규칙은 매치를 기록하지만 현재 원래 청크를 전달합니다 — 리댁션이 아직 와이어에서 다시 쓰이지 않음을 알고 작성하세요. 출력 block은 스트림에서 완전히 강제됩니다.
이 페이지는 OpenAI chat-completions SSE 형태를 설명합니다. 동일한 보류-평가-발행 계약이 형식별로 배선됩니다 — 네이티브 Anthropic Messages, Gemini, xAI, 그리고 OpenAI Responses 스트림 각각이 자체 이벤트 형태로 그것을 담습니다 — 그래서 어느 프로바이더가 요청을 서비스했는지와 무관하게 고객 관측 가능한 동작은 동일합니다.

6. 이것이 클라이언트에 의미하는 것

보류 프레임 모델의 몇 가지 실용적 결과:
유일한 툴 호출이 거부된 턴은 "tool_calls" 대신 finish_reason: "stop"으로 닫힙니다 — 에이전트에게는 “모델이 툴을 호출하지 않기로 선택”한 것으로 읽힙니다. 일부 호출이 살아남은 턴은 "tool_calls"로 닫히며, 살아남은 것만 담습니다.
업스트림이 firewall이 보류한 같은 종착 청크에 토큰 usage를 묶으면, 게이트웨이는 그것을 최종 재구성된 프레임에 다시 붙입니다 — 스트림 usage를 요청한 클라이언트는 여전히 그것을 받습니다.
모델이 같은 프레임에서 콘텐츠 툴 호출을 발행했으면, 툴 호출이 벗겨질 때조차 콘텐츠가 복구되고 재발행됩니다 — 한 호출을 차단하는 것이 결코 어시스턴트 텍스트를 드롭하지 않습니다.
스트림을 이 중 어느 것에도 옵트인하지 않습니다. 정책을 키에 연결하거나(또는 워크스페이스 기본값 설정) 이전과 정확히 동일하게 계속 스트리밍하세요 — 강제는 게이트웨이에 있습니다.

다음으로 갈 곳

스테이지 & 표면

inbound, response, mcp, egress — 각 규칙이 평가되는 곳.

Verdicts

allow, audit, deny, sanitize, pending_approval, cap_cost.

인자 정화

툴 호출의 인자에서 시크릿을 가림 — 인자 계층 전용.

Shadow mode

영향을 측정하는 동안 강제 판정을 audit로 강등합니다.
이것이 요청 경로의 어디에 위치하는지는 OrcaRouter가 검사하는 방식강제 경로 지연을 참조하세요. response 표면 강제가 봉쇄하는 위협은 위험한 툴 호출데이터 유출을 참조하세요. 전체 규칙 문법은 firewall 규칙 레퍼런스를 참조하세요.