GLiNER Guard: один энкодер вместо зоопарка LLM-гардрейлов

Если вы хоть раз собирали безопасный контур вокруг LLM в проде, картина знакомая: один моделью ловите PII, другой — токсичность, третий — попытки джейлбрейка, четвёртый — вредные инструкции, и где-то сбоку ещё классификатор интента. На один пользовательский запрос набегает десяток forward-проходов, каждый со своим форматом ответа, своими порогами и своим графиком деградации. У меня в команде это давно болевая точка: контур безопасности по стоимости и латентности нередко стоит дороже самой основной модели, а поддерживать этот зоопарк — отдельная работа на полставки.

Мы недавно разобрали GLiNER Guard — подход, который пытается схлопнуть весь этот набор классификаторов в один schema-driven энкодер. Идея звучит почти подозрительно просто, поэтому захотелось разложить её по полочкам и зафиксировать, что там реально работает, а где есть честные компромиссы.

Проблема зоопарка

Типичный гардрейл-стек — это пять-шесть специализированных моделей: модерация, детектор PII, классификатор вреда, детектор инъекций в промпт, анализ тональности. Каждая обучена под свою задачу, у каждой свой словарь меток и своя кривая. На практике это означает порядка 20 forward-проходов на один запрос, плюс склейку разнородных ответов в общий вердикт. Масштабировать такое дорого, а добавить новую политику — значит либо дообучать очередную модель, либо прикручивать ещё один сервис.

Что такое GLiNER Guard

В основе — GLiNER, encoder-модель для zero-shot NER. Вместо того чтобы быть зашитой под фиксированный набор классов, она принимает схему: вы перечисляете типы сущностей и метки классификации (опционально с описаниями), а модель размечает текст под эту схему без дообучения. GLiNER Guard переносит этот трюк на безопасность: одна модель одновременно достаёт сущности, выносит вердикт safe/unsafe, распознаёт типы атак, интент и тон.

В статье разобраны несколько вариантов. Lightweight на бэкбоне mmBERT-small — мультиязычный, заявлена поддержка 1800+ языков. Omni на mDeBERTa — лучше обобщает и на safety-, и на не-safety-задачах. И bi-encoder, который кэширует эмбеддинги меток отдельно — удобно, когда схема фиксирована и хочется максимума throughput.

Цифры, ради которых всё затевалось

Здесь самое интересное. Автогрегрессионный WildGuard на A100 выдаёт около 0.744 секунды на запрос — это примерно 1.3 запроса в секунду. GLiGuard в варианте bi-encoder на том же железе — 54 запроса в секунду. По качеству Omni даёт в среднем 76.9 F1 на бенчмарках Aegis 2.0, StrongReject и PolyGuard, обгоняя Llama-Guard 3 8B и подбираясь к топовым reasoning-моделям. На StrongReject — 99.7 F1.

По PII отдельная история. На англоязычном PII Masking — 0.804 F2 против 0.887 у специализированного SOTA, то есть проигрыш примерно в 8%. Зато на мультиязычном OpenPII (23 языка) — 0.930 F2, и здесь GLiGuard уже опережает и gliner-pii-nvidia, и privacy-фильтр OpenAI. Логика честная: на одном английском узкая модель всё ещё точнее, но как только данные мультиязычные — компромисс исчезает.

Как это выглядит в коде

Вся прелесть в том, что политика — это просто схема, которую можно менять на лету, без переобучения:

from gliner2 import GLiNER2

model = GLiNER2.from_pretrained("hivetrace/gliner-guard-omni")

schema = (
    model.create_schema()
    .entities(entity_types=["name", "email", "phone"], threshold=0.4)
    .classification(task="safety", labels=["safe", "unsafe"])
)

result = model.extract(user_text, schema=schema)
# {'entities': {...}, 'safety': 'unsafe'}

Набор меток богатый: сущности (person, email, phone, address), вердикт safe/unsafe, типы атак (instruction override, jailbreak persona, data exfiltration, роль-плей), классы вреда (опасные инструкции, харассмент, hate speech, мошенничество, оружие), интент и тон. Всё это — за один проход одной модели.

Моя практическая оценка

Один энкодер вместо пяти классификаторов и 4 forward-прохода вместо 20 — это не про доли процента качества, это про то, что контур безопасности перестаёт быть самой дорогой частью пайплайна.

Что мне нравится здесь как практику: единый формат ответа. Половина боли в проде — не сами модели, а склейка их разнородных вердиктов и поддержка пяти отдельных деплоев. Zero-shot смена политики через схему снимает классический цикл «новое требование комплаенса → дообучение → релиз»: вы просто правите список меток.

Чего я не стал бы делать — слепо выкидывать всё узкоспециализированное. На чисто английском PII те самые 8% F2 могут оказаться критичны, если у вас регуляторика и цена ошибки высокая. Я бы смотрел на GLiNER Guard как на сильный дефолт первой линии: дешёвый, быстрый, мультиязычный фильтр, который ловит подавляющее большинство кейсов, а узкие модели оставлял точечно — там, где они реально дают разницу. Для большинства продуктовых сценариев один schema-driven энкодер закрывает задачу честно и заметно дешевле, и именно ради этого расклада стоит его пробовать.

Полный разбор — в нашей статье на Хабре: GLiNER Guard: один schema-driven энкодер вместо зоопарка LLM-гардрейлов.

← ко всем записям