Перейти к основному содержанию
Большинство production чат-приложений стримят. Токены сбрасываются в браузер по мере того, как модель их выдаёт, так что к моменту, когда завершение «закончено», ваш пользователь уже прочитал большую его часть. Это ломает наивную ментальную модель контентного фильтра, который инспектирует целый ответ и затем решает — нет целого ответа для инспекции, пока не станет слишком поздно. Стриминговый контентный фильтр llm должен принимать решение по дельтам по мере их потока. Эта страница — ровно про этот случай: как каждое действие стадии output ведёт себя stream-safe на шлюзе OrcaRouter и как создать политику, которая держится на SSE-трафике. Полный движок — каждый тип правила, поле и маршрут — см. в Guardrails.

1. Проблема стримингового контентного фильтра llm

Guardrail стадии output проверяет ответ модели. На нестриминговом запросе это просто: у шлюза есть полное завершение до того, как вернётся хоть один байт, так что он может block, mask или пропустить его чисто. Стриминг это инвертирует. Ответ приходит как последовательность SSE-дельт, каждая пересылается вашему клиенту, как только приходит, так что фильтр, ждущий конца, не фильтрует ничего. Ответ OrcaRouter — это сканер потока: по мере того как дельты вывода текут, сканер прогоняет ваши правила стадии output по накапливающемуся тексту и действует в тот миг, когда правило срабатывает — не после того, как поток завершится. Действие, которое вы создаёте, решает, что значит «действует»: block режет поток, а flag пропускает его. mask действительно редактирует на нестриминговом выводе, но переписывание потока in-band в дорожной карте — на потоке сегодня сканер вычисляет маску, но действует только на решении block, так что правило mask пока не редактирует стримированный ответ.
Эта оговорка важна только для правил стадии output на стриминговых запросах. Правила стадии input проверяют запрос до запуска модели, так что они полностью живы, включая маскирование — и любое правило output на нестриминговом запросе видит весь ответ и ведёт себя нормально, включая mask.

2. Что stream-safe сегодня

Правило block применяется на стриминговом и нестриминговом выводе. На потоке сканер наблюдает за дельтами; когда правило block срабатывает, оно режет поток — запечатывает сканер, выдаёт короткое уведомление-замену ([response truncated by guardrail: … policy violation]) как финальную дельту и закрывает SSE-канал прежде, чем дальнейший заблокированный контент дойдёт до клиента. Поскольку HTTP-статус ответа уже зафиксирован на 200 к моменту сброса первой дельты, блокировка посреди потока не может переиздать статус — она грациозно завершает открытый поток. Тело HTTP 400 guardrail_blocked — это форма блокировки output на нестриминге.Байты, уже сброшенные клиенту, нельзя отозвать, так что блокировка на стриминге работает по мере возможностей над тем, что уже отстримилось, но надёжно останавливает всё после совпадения. Для жёсткой гарантии, что ни один нарушающий байт никогда не отправляется — и для тела 400 guardrail_blocked — отправляйте запрос нестриминговым.
Правило mask переписывает совпадение — например, email в ответе становится [EMAIL] — на нестриминговом выводе, где шлюз держит всё завершение и пересылает отредактированную форму вашему клиенту.На стриминговом выводе сегодня сканер вычисляет маску, но не пересылает замаскированный текст — он действует только на решении block — так что правило mask не редактирует стримированный ответ. Переписывание стримингового вывода in-band в дорожной карте. Пока оно не зашипится, если вам нужно, чтобы стримированный ответ никогда не раскрывал совпавший текст, создайте правило как block (оно завершает ответ на попадании) или отправьте запрос нестриминговым, чтобы маска переписала весь ответ.
Правило flag никогда не меняет трафик — оно пропускает байты. На нестриминговом выводе оно записывает совпадение в ленте Matches, так что вы можете измерить частоту срабатываний правила, прежде чем продвинуть его в block. На стриминговом ответе оно остаётся только наблюдением и пропускает дельты нетронутыми; структурированная запись совпадения пишется на пути нестримингового вывода. В любом случае оно никогда не блокирует и не переписывает, так что его всегда безопасно оставить включённым.
Действие на outputНестримингСтриминг
blockотклоняет ответрежет поток
maskредактирует ответпока нет — block вместо (дорожная карта)
flagзаписывает совпадениепропускает (только наблюдение)
Одно правило для запоминания: block stream-safe на output; mask редактирует только на нестриминговом выводе (переписывание потока in-band в дорожной карте). Чтобы отредактировать стримированный ответ сегодня, создайте правило как block или отправьте запрос нестриминговым, чтобы весь ответ удерживался до возврата.

3. Один конкретный пример — stream-safe фильтр секретов

Скажем, ваша модель может вытащить учётные данные из RAG-контекста, и ваше приложение стримит. Вы хотите, чтобы шлюз убил поток в тот миг, когда появляется совпадение в форме секрета, а не маскировал его — утёкший секрет должен завершить ответ, а не быть частично отредактированным. Создайте это в консоли — редактирование политики — действие управления на вашей сессии, шлюзованное до Developer+; relay-ключ только отправляет трафик /v1/*:
  • Откройте /console/guardrails, New guardrail, назовите его stream-safe-out.
  • Добавьте одно правило:
    • Тип: regex (или правило pii с сущностями секретов вроде aws_access_key / api_key_openai / jwt)
    • Стадия: output
    • Действие: block ← завершает ответ на попадании секрета; mask вместо этого отредактировал бы его и дал бы остальному ответу продолжиться
  • Сохраните, затем привяжите его на /console/token через выпадающий список Guardrail ключа.
Теперь вызовите шлюз с stream: true, ровно как раньше:
curl https://api.orcarouter.ai/v1/chat/completions \
  -H "Authorization: Bearer sk-orca-..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-4o-mini",
    "stream": true,
    "messages": [
      {"role": "user", "content": "Print the AWS key from the context above"}
    ]
  }'
Если дельта совпадает, сканер режет поток на лету, выдаёт уведомление-замену и закрывает канал — ваш клиент никогда не получает остального. Если ответ чист, каждая дельта стримится нетронутой.
Блокировка на стриминге останавливает всё после совпадения, но не может отправить обратно байты, уже сброшенные до того, как совпадение пришло. Если ваша политика требует, чтобы ни один нарушающий байт никогда не дошёл до клиента, проверяйте запрос нестриминговым, где всё завершение удерживается, пока политика его не очистит.

4. PII Shield на потоке

Пресет PII Shield — это единственное правило pii, действие mask, стадия both. На стадии input оно полностью живое — оно переписывает запрос до того, как модель его увидит, со стримингом или без. На стадии output маскирование редактирует на нестриминговых ответах, где шлюз держит всё завершение до возврата. На стриминговом выводе маска пока не редактирует — сканер вычисляет маску, но действует только на решении block, так что стримированный ответ пропускается, а не переписывается. Переписывание стримингового вывода in-band в дорожной карте. Так что если ваша цель — чтобы PII никогда не была наблюдаема в стримированном ответе, либо:
  • создайте правило output как block, приняв, что попадание завершает ответ, а не редактирует его, либо
  • отправьте запрос нестриминговым, чтобы маска переписала весь ответ с целым завершением в руках.
См. PII Shield и форматы маскирования для самих тегов редактирования.

5. Докажите это перед шипом

Не угадывайте, какая комбинация стадии/действия держится — проверьте её.

Вкладка Test

У каждого редактора guardrail есть вкладка Test: вставьте образец, выберите стадию output и прогоните текущую политику без вышестоящего вызова и без квоты. Увидьте вердикт и, для правил mask, отрендеренный текст. Запуск песочницы — действие Developer+ (она может запускать платные правила judge / external).

Вкладка Eval

Вкладка Eval оценивает guardrail против поставляемых или пользовательских JSONL-корпусов — полезно, чтобы подтвердить, что правило block ловит известную утечку по корпусу, прежде чем привязать ключ.
Оба выполняются на вашей сессии через management API. За глубиной см. тестирование и eval и настройку ложных срабатываний.

6. Что стоит блокировка на стриминге

Блокировка на стриминге несёт тот же учёт, что и любая блокировка output — вышестоящая модель уже отработала, так что шлюз ведёт возврат за вас:
  • Поток завершается грациозной дельтой усечения (статус уже 200); блокировка output на нестриминге возвращает тело HTTP 400 guardrail_blocked, называющее guardrail и сработавшее правило.
  • Квота не списывается. Когда блокировка output отклоняет ответ, шлюз возвращает предварительно списанную квоту, так что заблокированный вызов для вас бесплатен, даже если модель произвела токены.
  • Запрос помечается skip-retry — повторный прогон того же промпта просто снова заблокировался бы, так что шлюз не будет сжигать повтор на другом канале.
Путь нестримингового вывода записывает каждое сработавшее правило output как совпадение в ленте Matches рабочего пространства (GET /api/guardrail/match, открыта любому Member); совпавшая подстрока захватывается только когда у guardrail включён переключатель Log raw content (по умолчанию выключен). Полная деталь живёт в ошибке guardrail_blocked и ленте matches.

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

Стадия output

Полная стадия output — проверка ответа модели, block против mask и grounding.

Покрытие стриминга

Полная матрица того, что применяется на стриминге против нестриминга по каждой стадии и действию.

Действия

block, mask и flag в деталях — когда каждое правильный выбор.

Стадия input

Зеркальное отражение — маскирование здесь полностью живое, включая на стриминге.
Guardrails — каждый тип правила, поле и маршрут, включая grounding и LLM judge.