deny ou sanitize nas chamadas de ferramenta que seu modelo emite — e seu
agente chama o gateway com "stream": true. A pergunta que de fato importa:
uma resposta em streaming pode vazar uma chamada de ferramenta bloqueada antes
de o firewall decidir? Não pode, e esta página explica o único mecanismo que
torna isso verdade para que você possa raciocinar sobre latência e os chunks que
seu cliente recebe.
Este é um olhar focado no comportamento SSE. Para os vereditos em si veja
Vereditos; para a gramática de regras veja a
referência de regras.
1. O problema do firewall sse em streaming
Uma resposta não-streaming é um corpo JSON — o firewall vê a coisa inteira, avalia ostool_calls, e retorna o resultado limpo. Um stream é diferente:
um modelo emite uma chamada de ferramenta como dezenas de deltas de tool_call
através de muitos frames SSE, e uma vez que um frame é encaminhado, seu agente
já o tem — não há como retrair um token que você enviou. Avalie cedo demais e
você não tem a chamada completa (nome + argumentos completos) para julgar;
encaminhe conforme avança e um deny já é tarde demais.
O gateway resolve isso com um contrato simples e observável:
Conteúdo faz streaming ao vivo
Deltas normais de texto e raciocínio passam inalterados, em tempo real —
zero latência adicionada nos tokens que seu usuário lê.
Frames de chamada de ferramenta são retidos
Qualquer frame carregando um delta de
tool_call (ou function_call legado)
é retido do stream ao vivo até a chamada estar completa e avaliada.O firewall é um portão de segurança, então ele parseia cada frame. Ele não
adivinha que um frame é só-conteúdo a partir dos bytes crus — um membro
tool_calls escapado em JSON não tem substring literal para corresponder, então
um atalho de substring encaminharia uma chamada de ferramenta não avaliada. Os
frames SSE são pequenos; o portão parseia cada um.2. A sequência reter-montar-avaliar
Para uma resposta de chat-completions em streaming com uma política de superfície response ativa, cada frame que o upstream emite toma um de dois caminhos:Frame de content / role / reasoning / usage → encaminhado agora
Frame de content / role / reasoning / usage → encaminhado agora
Faz streaming até seu cliente imediatamente, byte-a-byte. Esses nunca carregam
uma chamada de ferramenta, então o firewall não tem nada a decidir.
Frame de tool_call (ou function_call legado) → retido
Frame de tool_call (ou function_call legado) → retido
Bufferizado fora do stream ao vivo. O frame de fechamento
finish_reason de
um turno de ferramenta é retido junto com ele, porque emiti-lo cedo diria ao
seu cliente que o turno acabou antes de o firewall ter decidido.arguments transmitidos de cada chamada),
avalia cada uma contra sua política na superfície response — a mesma semântica
de veredito e regra que o caminho não-streaming — e emite apenas os
sobreviventes:
| Veredito da chamada retida | O que seu cliente recebe |
|---|---|
allow / audit | Os frames retidos originais, inalterados — uma passagem atrasada, não um chunk re-batcheado. |
sanitize | A chamada com seus argumentos reescritos (segredos/PII correspondidos substituídos por um token tipado), re-emitida. |
deny | A chamada é descartada. Se era a única chamada do turno, o turno fecha com finish_reason: "stop" — o stream parece como se o modelo não tivesse feito nenhuma chamada de ferramenta. |
3. Um exemplo concreto
Uma política de response com uma regradeny em *.delete (crie-a no editor de
regras do console) e uma requisição em streaming cujo modelo decide chamar tanto
db.query quanto db.delete:
db.query — db.delete foi montado, avaliado, negado e nunca emitido. A
chamada sobrevivente é re-indexada a partir de 0, e o evento de firewall para a
chamada negada aterrissa no seu log de events
com a regra que disparou.
4. Blocks inbound fazem curto-circuito antes de o stream começar
A dança de frames retidos é só para a superfície response — chamadas que o modelo emite. Um denyinbound (uma ferramenta
que um agente anuncia) dispara antes da chamada ao modelo upstream, então
uma requisição em streaming que aciona uma regra inbound nunca abre um stream SSE:
ela retorna um HTTP 400 simples com o código de erro firewall_blocked,
marcado como
skip-retry.
Sem frames, sem janela de retenção — o block aterrissa como qualquer erro
não-streaming.
5. Guardrails no mesmo stream
Uma resposta em streaming pode carregar uma política de saída de Guardrail e uma política de response de firewall ao mesmo tempo. Elas agem sobre coisas diferentes — os guardrails filtram o texto que o modelo transmite; o firewall governa as chamadas de ferramenta — e elas se compõem:- Block de guardrail de saída (streaming): o scanner de saída corta o stream
no momento em que uma regra dispara, encaminha um único chunk genérico de
substituição —
[Response blocked by content policy.]comfinish_reason: "content_filter"— e para. A mensagem é deliberadamente genérica (sem categoria de regra) para que um sondador não possa enumerar sua política. Uma retenção de firewall em andamento quando isso acontece é descartada, então uma chamada de ferramenta retida não pode escapar depois do block. - Mask de guardrail de saída (streaming): o masking da requisição antes do modelo está ativo; o masking in-band ao vivo da saída em streaming está no roadmap. Num stream uma regra de mask registra a correspondência mas atualmente encaminha o chunk original — crie-a sabendo que a redação ainda não é reescrita na prática. O block de saída é totalmente aplicado em streams.
Esta página descreve a forma SSE de chat-completions da OpenAI. O mesmo
contrato reter-avaliar-emitir é cabeado por formato — Anthropic Messages nativo,
Gemini, xAI e o stream de OpenAI Responses cada um o carrega em sua própria forma
de evento — então o comportamento observável pelo cliente é idêntico
independentemente de qual provedor atendeu a requisição.
6. O que isso significa para o seu cliente
Algumas consequências práticas do modelo de frames retidos:finish_reason pode mudar
finish_reason pode mudar
Um turno cuja única chamada de ferramenta foi negada fecha com
finish_reason: "stop" em vez de "tool_calls" — para o seu agente lê-se
como “o modelo escolheu não chamar uma ferramenta”. Um turno onde algumas
chamadas sobreviveram fecha com "tool_calls", carregando apenas os
sobreviventes.usage ainda chega
usage ainda chega
Quando um upstream empacota o
usage de token no mesmo chunk terminal que o
firewall reteve, o gateway o re-anexa ao frame reconstruído final — um cliente
que pediu usage de stream ainda o recebe.texto que compartilhou um chunk de chamada de ferramenta é preservado
texto que compartilhou um chunk de chamada de ferramenta é preservado
Se o modelo emitiu conteúdo e uma chamada de ferramenta no mesmo frame, o
conteúdo é recuperado e re-emitido mesmo quando a chamada de ferramenta é
removida — bloquear uma chamada nunca descarta o texto do seu assistente.
nenhuma mudança no código do agente
nenhuma mudança no código do agente
Você não opta um stream para nada disso. Vincule uma política à chave (ou
defina um padrão do workspace) e continue fazendo streaming exatamente como
antes — o enforcement está no gateway.
Para onde ir a seguir
Stages e superfícies
inbound, response, mcp, egress — onde cada regra avalia.
Vereditos
allow, audit, deny, sanitize, pending_approval, cap_cost.
Sanitizar argumentos
Redija segredos dos argumentos de uma chamada de ferramenta — apenas na camada
de argumento.
Shadow mode
Rebaixe vereditos de enforcement para audit enquanto você mede o impacto.
