Решения
Guardrails для LLM: фильтрация, PII и аудит
Задача страницы. Дать практический план, как защитить LLM‑сервисы: входная/выходная фильтрация, PII‑санитайзинг, защита от prompt‑injection, контроль инструментов (function calling), аудит и метрики. Подходит для чатов, RAG, агентных систем и API‑сервисов.
TL;DR
- Стройте конвейер Pre → Policy → LLM → Post → Audit.
- На входе: классификация запросов, маскирование PII, детектор jailbreak/injection.
- Внутри: строгий JSON‑контракт для инструментов и allow‑list функций.
- На выходе: PII‑редакция, фильтры категорий, лимиты длины/тайм‑ауты, стриминг с heartbeat.
Логируйте события, а не сырые данные: маскируйте PII, хэшируйте идентификаторы.
Связанные разделы: /solutions/security/, /solutions/llm-inference/agents/, /solutions/llm-inference/streaming/, /solutions/llm-inference/observability/, /solutions/monitoring-logging/.
Угрозы и цели
Угрозы
- Утечки и обработка персональных данных (PII).
- Prompt‑injection/jailbreak: инструкции, заставляющие модель игнорировать политику.
- Инструменты: опасные сайд‑эффекты (запись в БД, внешние API) без валидации.
- Токсичный контент/несоблюдение правил продукта.
- Отсутствие трейсинга и воспроизводимости инцидентов.
Цели
- Минимизировать риск утечек и нарушений политик.
- Сохранить UX (стриминг, низкая латентность) при разумной цене.
- Обеспечить аудит, воспроизводимость и метрики качества/безопасности.
Архитектура Guardrails: Pre → Policy → LLM → Post → Audit
[Request]
└─ Pre-Filter → Policy/Prompt Hardening → LLM/Tools → Post-Filter/PII Redact → Audit/Logs
- Pre‑Filter: классификация темы/рисков, PII‑маскирование, проверка длины, rate‑limit.
- Policy/Prompt Hardening: системные правила, JSON‑режим, ограничения инструментов.
- LLM/Tools: сервинг в выбранном стеке (vLLM/TGI/…); строгое function calling.
- Post‑Filter: проверка ответа, редакция PII/категорий, обрезка, тайм‑ауты.
- Audit: безопасные логи событий, метрики.
См. также: /solutions/llm-inference/, /solutions/llm-inference/multi-model/.
Входные фильтры: PII, категории, injection

Классификация/правила
- Лёгкий классификатор или эвристики → метки category = {general, code, finance, medical, unknown}, risk = {low, medium, high}.
- Для high — ужесточайте лимиты: max_tokens, включайте строгие шаблоны, раздельные пулы (см. /solutions/llm-inference/multi-model/).
Детектор PII (маскирование до LLM)
- E‑mail, телефоны (+7 / международный формат), паспорт/карта (проверка Luhn), адреса, ИНН и т. п.
- Заменяйте на токены‑маркеры: <EMAIL_1>, <PHONE_1>, <CARD_1>; храните оригиналы отдельно (если нужно использовать инструмент).
Пример маскировки (упрощённо, Python):
import re
RE_EMAIL = re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}")
RE_PHONE = re.compile(r"(\+?\d[\d\-\s()]{7,}\d)")
RE_CARD = re.compile(r"\b(?:\d[ -]*?){13,19}\b")
def luhn_ok(s):
digits = [int(c) for c in re.sub(r"\D", "", s)]
if len(digits) < 13: return False
checksum = 0
dbl = False
for d in digits[::-1]:
checksum += (d*2 - 9) if dbl and d > 4 else (d*2 if dbl else d)
dbl = not dbl
return checksum % 10 == 0
def sanitize(text):
m = {}
def repl_email(mo):
i = len(m.get('email', [])); m.setdefault('email', []).append(mo.group(0)); return f""
def repl_phone(mo):
i = len(m.get('phone', [])); m.setdefault('phone', []).append(mo.group(0)); return f""
def repl_card(mo):
raw = mo.group(0)
if not luhn_ok(raw): return raw
i = len(m.get('card', [])); m.setdefault('card', []).append(raw); return f""
text = RE_EMAIL.sub(repl_email, text)
text = RE_PHONE.sub(repl_phone, text)
text = RE_CARD.sub(repl_card, text)
return text, m # text без PII + маппинг для инструментов
Prompt‑injection/jailbreak
- Ищите паттерны: «ignore previous», «disregard instructions», вставки системных ролей, попытки раскрытия ключей/политик, вложенный HTML/Markdown‑линки с «магическими» инструкциями.
- Для подозрительных — отбраковка/уточнение или строгий шаблон ответа.
Жёсткий режим инструментов (function calling)
- Allow‑list функций; JSON Schema для аргументов; валидация и тайм‑ауты на исполнение.
- Сайд‑эффектные инструменты (запись/платёж) — только после явного подтверждения политики с контекстом.
- В ответах модели запрещён произвольный текст: только JSON с tool_name и arguments.
Подробно: /solutions/llm-inference/agents/.
Валидация аргументов (псевдокод):
def call_tool(name, args):
spec = TOOLS_REGISTRY[name] # содержит JSON Schema
validate(args, spec.schema) # выбросить ошибку при несоответствии
return execute(name, args, timeout=8.0) # с таймаутом и retry/backoff
Пост‑фильтрация ответов
- PII‑редакция: повторно маскируйте PII в тексте ответа, если передача наружу запрещена.
- Категории: токсичность/ненависть/насилие — блок/перефраз.
- Обрезка: принудительный max_tokens/max_chars, трим по фразе/предложению.
- Политики домена: юридические, мед, фин — выводить безопасные шаблоны или краткие инструкции вместо конкретных рецептов.
- Стриминг: при срабатывании пост‑фильтра отправляйте корректное завершение потока (сигнал finish_reason=»policy»). См. /solutions/llm-inference/streaming/.


Лимиты, тайм‑ауты, деградация
- Лимиты длины: max context, max_tokens, лимит на вложения RAG.
- Тайм‑ауты: prefill, межтокенный idle, общий.
- Деградация: при перегрузе — снижение max_tokens, переключение в short‑pool, фоллбек‑модель. См. /solutions/llm-inference/multi-model/.
Логи и аудит: события вместо «сырья»
Принципы
- Логируйте события и метаданные, а не полные тексты.
- Маскируйте PII и секреты на входе; используйте хэш‑идентификаторы с солью.
- Шифруйте хранение и ограничивайте доступ (RBAC). См. /solutions/security/.
Схема события (JSON)
{
"ts": "2025-08-22T12:34:56Z",
"tenant": "acme",
"user_hash": "u_9f3c...",
"model": "llama3-8b@int8",
"route": "short_pool",
"risk": "low",
"input_meta": {"len": 512, "pii": ["EMAIL","PHONE"]},
"policy": {"json_only": true, "max_tokens": 256},
"tools": [{"name":"search","ok":true,"latency_ms":120}],
"stream": {"ttft_ms":180, "tbt_ms":25, "ttlt_ms":1200},
"output_meta": {"len": 420, "pii_redacted": true, "finish_reason": "stop"},
"decision": {"blocked": false, "reason": null},
"trace_id": "tr_abc123"
}
Метрики и дашборды
Минимум:
- TTFT/TBT/TTLT и p50/p95/p99.
- Blocked/flagged rate по категориям.
- PII hit rate (доли типов), false‑pos/false‑neg семплы.
- Ошибки инструментов и тайм‑ауты, средняя глубина agent‑цикла.
- KV‑кэш и GPU‑метрики для связи с деградациями.
См. /solutions/llm-inference/observability/, /solutions/monitoring-logging/.
Тесты и red‑teaming
- Набор негативов: jailbreak‑фразы, обфускации, Unicode‑трюки, длинные «ядовитые» промпты.
- Мутабельность: автоматическая генерация вариантов (перестановки, синонимы, разные кодировки).
- Nightly: регрессы по блок‑рейтам и качеству; отбор 1–5% трафика в оффлайн‑оценку.
Связанные разделы: /solutions/llm-training/eval/.
Интеграция со стримингом и API
- SSE/WS: события policy_start, policy_violation, tool_start, tool_result, done.
- Cancel должен немедленно завершать генерацию и освобождать ресурсы.
- Явно маркируйте финал: finish_reason = «stop» | «length» | «policy» | «cancelled».
Экономика: влияние guardrails на стоимость
Фильтры и сокращение контекста напрямую уменьшают L_in/L_out, тем самым:
T ≈ (L_in / P) + (L_out / (D / B)) + overhead
Cost_per_1M ≈ (GPU_hour_price × Num_GPU) / (TPS_decode × 3600 / 1e6)
Экономьте через: квантизацию (/solutions/llm-inference/quantization/), лимиты длины, short‑pool, prefix‑кэш (см. /solutions/llm-inference/vllm/), отказ/перефраз токсичного контента на ранней стадии.
Шаблон конвейера (FastAPI, упрощённо)
from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse
from typing import Dict
app = FastAPI()
def classify(text)->Dict: ...
def detect_injection(text)->bool: ...
def post_filter(text)->Dict: ... # {"ok":True, "text":..., "reason":...}
@app.post("/v1/chat/completions_stream")
async def serve(req: Request):
body = await req.json()
user_msg = body["messages"][-1]["content"]
# PRE
sanitized, mapping = sanitize(user_msg) # PII →
meta = classify(sanitized)
if detect_injection(sanitized):
return {"blocked": True, "reason": "injection"}
# POLICY (лимиты/JSON-режим/инструменты)
cfg = {"max_tokens": min(256, body.get("max_tokens", 256)), "json_only": False}
# LLM STREAM (обёртка над vLLM/TGI/…)
async def stream():
yield "event: start\ndata: {}\n\n"
async for delta in llm_stream(sanitized, cfg):
# POST
pf = post_filter(delta)
if not pf["ok"]:
yield 'data: {"finish_reason":"policy"}\n\n'
break
yield f'data: {{"choices":[{{"delta":{{"content":"{pf["text"]}"}}}}]}}\n\n'
yield "event: done\ndata: [DONE]\n\n"
return StreamingResponse(stream(), media_type="text/event-stream",
headers={"Cache-Control":"no-cache","X-Accel-Buffering":"no"})
Как запускать это в cloudcompute.ru
- Шаблон “Guardrails‑Gateway” в /solutions/templates/:
– входные фильтры (PII/jailbreak), жёсткий режим инструментов, лимиты/тайм‑ауты;
– SSE/WS‑стриминг с событиями;
– логи событий и готовые дашборды. - Дополнения: безопасность — /solutions/security/; наблюдаемость — /solutions/llm-inference/observability/; стоимость — /solutions/cost-planner/, /solutions/llm-inference/costs/.

- Входные фильтры: PII‑маскирование, категория/риск, детектор injection.
- Политики: системный промпт, JSON‑контракт инструментов, allow‑list.
- Лимиты/тайм‑ауты: max context, max_tokens, prefill/idle/overall, cancel.
- Пост‑фильтры: PII‑редакция, категории, обрезка, finish_reason.
- Аудит: события, маски PII, шифрование, RBAC, дашборды.
- Деградация и фоллбеки: short‑pool, меньшая модель, снижение лимитов.
- Nightly‑тесты/ред‑тиминг и отслеживание регрессов.
Навигация по разделу «Инференс LLM»
/solutions/llm-inference/ • /solutions/llm-inference/vllm/ • /solutions/llm-inference/tgi/ • /solutions/llm-inference/tensorrt-llm/ • /solutions/llm-inference/sglang/ • /solutions/llm-inference/llama-cpp/ • /solutions/llm-inference/agents/ • /solutions/llm-inference/streaming/ • /solutions/llm-inference/observability/ • /solutions/monitoring-logging/ • /solutions/security/ • /solutions/llm-inference/quantization/