Перейти к основному содержанию
Вы включили правило поверхности responsedeny или sanitize на вызовах инструментов, которые выдаёт ваша модель — и ваш агент вызывает шлюз с "stream": true. Вопрос, который реально важен: может ли стриминговый ответ утечь заблокированный вызов инструмента до того, как firewall решит? Не может, и эта страница объясняет один механизм, который делает это правдой, так чтобы вы могли рассуждать о задержке и чанках, которые получает ваш клиент. Это сфокусированный взгляд на поведение SSE. О самих вердиктах см. Вердикты; о грамматике правил см. справочник правил.

1. Проблема стримингового firewall sse

Нестриминговый ответ — это одно JSON-тело — firewall видит всё это целиком, вычисляет tool_calls и возвращает очищенный результат. Стрим другой: модель выдаёт вызов инструмента как десятки дельт tool_call через множество SSE-фреймов, и как только фрейм переслан, ваш агент его уже имеет — нельзя отозвать токен, который вы отправили. Вычислите слишком рано — и у вас нет полного вызова (имя + полные аргументы) для суждения; пересылайте по ходу — и deny уже слишком поздно. Шлюз решает это простым, наблюдаемым контрактом:

Контент стримится вживую

Обычные текстовые дельты и дельты рассуждения проходят без изменений, в реальном времени — нулевая добавленная задержка на токенах, которые читает ваш пользователь.

Фреймы вызовов инструментов удерживаются

Любой фрейм, несущий дельту tool_call (или legacy function_call), удерживается из живого стрима, пока вызов не завершён и не вычислен.
Firewall — это шлюз безопасности, так что он парсит каждый фрейм. Он не угадывает, что фрейм только-контентный, по сырым байтам — JSON-экранированный член tool_calls не имеет литеральной подстроки для сопоставления, так что подстрочный shortcut переслал бы невычисленный вызов инструмента. SSE-фреймы малы; шлюз парсит каждый из них.

2. Последовательность hold-assemble-evaluate

Для стримингового ответа chat-completions с активной политикой поверхности response каждый фрейм, который выдаёт upstream, идёт по одному из двух путей:
Стримится вашему клиенту немедленно, побайтно. Они никогда не несут вызов инструмента, так что firewall’у нечего решать.
Буферизуется вне живого стрима. Закрывающий фрейм finish_reason хода с инструментом удерживается вместе с ним, потому что выдача его рано сказала бы вашему клиенту, что ход окончен, до того как firewall вынес решение.
В конце стрима шлюз собирает удержанные фреймы в полные вызовы инструментов (соединяя стримированные фрагменты arguments каждого вызова), вычисляет каждый против вашей политики на поверхности responseта же семантика вердикта и правила, что у нестримингового пути — и выдаёт только выживших:
Вердикт удержанного вызоваЧто получает ваш клиент
allow / auditОригинальные удержанные фреймы, без изменений — отложенный pass-through, не пере-собранный в батч чанк.
sanitizeВызов с переписанными аргументами (совпавшие секреты/PII заменены типизированным токеном), повторно выданный.
denyВызов отбрасывается. Если это был единственный вызов хода, ход закрывается с finish_reason: "stop" — стрим выглядит так, будто модель не сделала вызова инструмента.
Если ничего не совпало, вы платите только задержку буферизации на фреймах вызова инструмента — контент уже стримился вживую. Firewall реконструирует фреймы только когда реально действует (deny или sanitize); чистый allow пересылает точные байты вашего upstream.

3. Один конкретный пример

Политика response с правилом deny на *.delete (создайте его в консольном редакторе правил) и стриминговый запрос, чья модель решает вызвать оба db.query и db.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.querydb.delete был собран, вычислен, отклонён и никогда не выдан. Выживший вызов переиндексируется с 0, а событие firewall для отклонённого вызова приземляется в вашем журнале событий с правилом, которое сработало.
Сначала разверните стриминговую политику response под shadow-режимом. В shadow-режиме каждый применяющий вердикт понижается до audit (причина с префиксом [shadow] would …), и все фреймы вызовов инструментов проходят — так что вы можете подтвердить, что политика совпадает с тем, что вы ожидаете, на реальном стримированном трафике, до того как она начнёт отбрасывать вызовы.

4. Inbound-блокировки замыкаются накоротко до начала стрима

Танец с удержанными фреймами — только для поверхности response — вызовов, которые модель выдаёт. inbound deny (инструмент, который агент рекламирует) срабатывает до вызова вышестоящей модели, так что стриминговый запрос, который срабатывает inbound-правилом, вообще никогда не открывает SSE-стрим: он возвращает обычный HTTP 400 с кодом ошибки firewall_blocked, помеченный skip-retry. Нет фреймов, нет окна удержания — блокировка приземляется как любая нестриминговая ошибка.

5. Guardrails на том же стриме

Стриминговый ответ может нести политику вывода Guardrail и политику response firewall одновременно. Они действуют на разные вещи — guardrails проверяют текст, который стримит модель; firewall управляет вызовами инструментов — и они компонуются:
  • Блокировка вывода guardrail (стриминг): сканер вывода обрезает стрим в тот момент, когда правило срабатывает, пересылает единственный обобщённый чанк замены — [Response blocked by content policy.] с finish_reason: "content_filter" — и останавливается. Сообщение намеренно обобщённое (нет категории правила), так что зондировщик не может перечислить вашу политику. Удержание firewall в полёте, когда это происходит, отбрасывается, так что удержанный вызов инструмента не может проскользнуть после блокировки.
  • Маскировка вывода guardrail (стриминг): маскировка запроса до модели живая; живая in-band маскировка стримированного вывода в дорожной карте. На стриме правило маскировки записывает совпадение, но в настоящее время пересылает оригинальный чанк — создавайте его, зная, что редактирование пока не переписывается на проводе. Output block полностью применяется на стримах.
Эта страница описывает форму SSE OpenAI chat-completions. Тот же контракт hold-evaluate-emit подключён по форматам — native Anthropic Messages, Gemini, xAI и стрим OpenAI Responses каждый несут его в собственной форме событий — так что наблюдаемое клиентом поведение идентично независимо от того, какой провайдер обслужил запрос.

6. Что это значит для вашего клиента

Несколько практических следствий модели удержанных фреймов:
Ход, чей единственный вызов инструмента был отклонён, закрывается с finish_reason: "stop" вместо "tool_calls" — для вашего агента это читается как «модель решила не вызывать инструмент». Ход, где выжили некоторые вызовы, закрывается с "tool_calls", неся только выживших.
Когда upstream упаковывает usage токенов в тот же терминальный чанк, который firewall удержал, шлюз повторно прикрепляет его к финальному реконструированному фрейму — клиент, запросивший usage стрима, всё равно его получает.
Если модель выдала контент и вызов инструмента в том же фрейме, контент восстанавливается и повторно выдаётся, даже когда вызов инструмента убран — блокировка одного вызова никогда не отбрасывает ваш текст ассистента.
Вы не подключаете стрим ни к чему из этого. Привяжите политику к ключу (или задайте default рабочего пространства) и продолжайте стримить ровно как раньше — применение на шлюзе.

Куда двигаться дальше

Стадии и поверхности

inbound, response, mcp, egress — где вычисляется каждое правило.

Вердикты

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

Очистка аргументов

Отредактировать секреты из аргументов вызова инструмента — только уровень аргументов.

Shadow-режим

Понизить применяющие вердикты до audit, пока измеряете влияние.
О том, где это сидит в пути запроса, см. как OrcaRouter инспектирует и задержку пути применения. Об угрозах, которые сдерживает применение поверхности response, см. опасные вызовы инструментов и эксфильтрацию данных. О полной грамматике правил см. справочник правил firewall.