LLM-ограничители на практике: что действительно работает
«Контролируйте риски, а не только модель»
Языковые модели (LLM) непредсказуемы. Они галлюцинируют, утекают данные, генерируют вредоносный контент или отказываются выполнять законные запросы. Ограничительные механизмы (guardrails) сужают поведение модели, не снижая при этом её возможностей.
Главное — знать, какие ограничения действительно важны, а какие являются лишь шумом.
Ограничения — это не про контроль над моделью. Это про управление рисками.

Валидация входных данных
Самое важное ограничение. Плохой ввод приводит к плохому выводу, а также может спровоцировать инъекцию промптов в вашу систему.
Стратегия 1: Санитизация промптов
Удаляйте опасные паттерны на раннем этапе:
import re
class PromptSanitizer:
def __init__(self):
self.dangerous_patterns = [
r"ignore\s+previous\s+instructions",
r"system\s+prompt",
r"you\s+are\s+now\s+free",
r"break\s+out\s+of",
]
def sanitize(self, prompt: str) -> str:
for pattern in self.dangerous_patterns:
prompt = re.sub(pattern, "[REDACTED]", prompt, flags=re.IGNORECASE)
return prompt
Это не делает систему непробиваемой. Противниковые входы бывают креативными. Но этот метод ловит очевидные угрозы, а они — самые частые.
Стратегия 2: Ограничение длины ввода
Ограничения длины предотвращают расход токенов и таймауты:
class InputValidator:
def __init__(self, max_length: int = 10000):
self.max_length = max_length
def validate(self, prompt: str) -> tuple[bool, str]:
if len(prompt) > self.max_length:
return False, f"Input too long: {len(prompt)} > {self.max_length}"
return True, "OK"
Стратегия 3: Фильтрация контента
Фильтрация контента блокирует нарушения политик. Паттерны здесь зависят от вашей предметной области:
class ContentFilter:
def __init__(self):
self.blocked_topics = [
"violence", "hate speech", "self-harm",
"sexual content", "illegal activities",
]
def filter(self, prompt: str) -> tuple[bool, str]:
prompt_lower = prompt.lower()
for topic in self.blocked_topics:
if topic in prompt_lower:
return False, f"Blocked: {topic}"
return True, "OK"
Простое совпадение строк работает быстро, но неточно. Для продакшена используйте модель-классификатор — даже небольшую, вроде qwen3-1.7b — для обнаружения нарушений политик. Это точнее и сложнее для обхода.
Фильтрация выходных данных
Вывод модели тоже нужно проверять. Структуру, контент и факты.
Стратегия 1: Валидация ответов
Сначала валидируйте структуру. Если вы ожидаете JSON, проверяйте JSON:
class ResponseValidator:
def __init__(self):
self.required_fields = ["answer", "confidence"]
def validate(self, response: dict) -> tuple[bool, str]:
for field in self.required_fields:
if field not in response:
return False, f"Missing field: {field}"
return True, "OK"
Стратегия 2: Фильтрация контента
Фильтруйте вредоносный контент:
class OutputFilter:
def __init__(self):
self.blocked_patterns = [
r"kill\s+someone",
r"bomb\s+recipe",
r"hate\s+speech",
r"self-harm",
]
def filter(self, response: str) -> tuple[bool, str]:
for pattern in self.blocked_patterns:
if re.search(pattern, response, re.IGNORECASE):
return False, f"Blocked: {pattern}"
return True, "OK"
Стратегия 3: Проверка фактов
Проверка фактов сложнее. Вы не можете валидировать каждое утверждение, поэтому выбирайте те, которые важны:
class FactChecker:
def __init__(self):
self.known_facts = {
"capital of france": "Paris",
"population of usa": "330 million",
"speed of light": "299,792,458 m/s",
}
def check(self, claim: str) -> tuple[bool, str]:
claim_lower = claim.lower()
for fact, truth in self.known_facts.items():
if fact in claim_lower and truth not in claim_lower:
return False, f"Fact check failed: {fact}"
return True, "OK"
Для реальной проверки фактов вам нужен конвейер поиска (retrieval pipeline). Проверяйте утверждения против базы знаний, а не против захардкоженного словаря.
Механизмы безопасности
Стратегия 1: Ограничение скорости (Rate Limiting)
Ограничение скорости предотвращает злоупотребления:
import time
from collections import deque
class RateLimiter:
def __init__(self, max_requests: int = 10, window: int = 60):
self.max_requests = max_requests
self.window = window
self.requests = deque()
def allow(self) -> bool:
now = time.time()
while self.requests and self.requests[0] < now - self.window:
self.requests.popleft()
if len(self.requests) >= self.max_requests:
return False
self.requests.append(now)
return True
Стратегия 2: Квотирование токенов
Квотирование токенов ограничивает затраты на запрос:
class TokenBudget:
def __init__(self, max_tokens: int = 1000):
self.max_tokens = max_tokens
def validate(self, response: str) -> tuple[bool, str]:
token_count = len(response.split())
if token_count > self.max_tokens:
return False, f"Token limit exceeded: {token_count} > {self.max_tokens}"
return True, "OK"
Стратегия 3: Управление окном контекста
Управление окном контекста предотвращает переполнение:
class ContextManager:
def __init__(self, max_context: int = 4096):
self.max_context = max_context
self.context = []
def add(self, message: str):
self.context.append(message)
self.trim()
def trim(self):
while len(" ".join(self.context)) > self.max_context:
self.context.pop(0)
Усечение скользящим окном просто, но теряет ранний контекст. Более продвинутые подходы используют суммаризацию или сжатие на основе внимания, но это добавляет задержку.
Соответствие требованиям (Compliance)
Корпоративным системам нужны ограничения для соответствия требованиям. Два самых важных:
Паттерн 1: Резидентность данных
Резидентность данных — обеспечьте, чтобы данные оставались в пределах требуемых географических границ:
class DataResidency:
def __init__(self, allowed_regions: list[str]):
self.allowed_regions = allowed_regions
def validate(self, region: str) -> tuple[bool, str]:
if region not in self.allowed_regions:
return False, f"Region not allowed: {region}"
return True, "OK"
Паттерн 2: Аудит-логирование
Аудит-логирование — логируйте все взаимодействия с моделью:
import json
from datetime import datetime
class AuditLogger:
def __init__(self, log_file: str = "audit.log"):
self.log_file = log_file
def log(self, request: dict, response: dict):
entry = {
"timestamp": datetime.now().isoformat(),
"request": request,
"response": response,
}
with open(self.log_file, "a") as f:
f.write(json.dumps(entry) + "\n")
Аудит-логи критически важны для отладки и соответствия требованиям. Делайте их структурированными, доступными только для добавления и храните в безопасности.
Сборка воедино
Паттерн 1: Простые ограничения
Простой конвейер ограничений:
class SimpleGuardrails:
def __init__(self):
self.input_validator = InputValidator(max_length=10000)
self.output_filter = OutputFilter()
def process(self, prompt: str) -> str:
valid, message = self.input_validator.validate(prompt)
if not valid:
return f"Error: {message}"
response = self.call_model(prompt)
valid, message = self.output_filter.filter(response)
if not valid:
return f"Error: {message}"
return response
Паттерн 2: Продвинутые ограничения
Продвинутые ограничения добавляют санитизацию, ограничение скорости и квоты токенов:
class AdvancedGuardrails:
def __init__(self):
self.sanitizer = PromptSanitizer()
self.input_validator = InputValidator(max_length=10000)
self.content_filter = ContentFilter()
self.output_filter = OutputFilter()
self.rate_limiter = RateLimiter(max_requests=10)
self.token_budget = TokenBudget(max_tokens=1000)
def process(self, prompt: str) -> str:
prompt = self.sanitizer.sanitize(prompt)
valid, message = self.input_validator.validate(prompt)
if not valid:
return f"Error: {message}"
valid, message = self.content_filter.filter(prompt)
if not valid:
return f"Error: {message}"
if not self.rate_limiter.allow():
return "Error: Rate limit exceeded"
response = self.call_model(prompt)
valid, message = self.output_filter.filter(response)
if not valid:
return f"Error: {message}"
valid, message = self.token_budget.validate(response)
if not valid:
return f"Error: {message}"
return response
Когда ограничения важны
Ограничения важны, когда вы создаете системы, ориентированные на пользователя, обрабатываете конфиденциальные данные или работаете в продакшене. Они также важны, когда у вас есть требования к соответствию — GDPR, HIPAA, SOC 2.
Они не важны, когда вы прототипируете, используете модели только для внутренних инструментов или не обрабатываете конфиденциальные данные. Пропускайте их, пока они вам не понадобятся.
Компромисс всегда заключается в балансе между возможностями и безопасностью. Больше ограничений означают меньше сбоев, но и меньше возможностей. Найдите баланс, который подходит для вашей системы.
Компромиссы
| Стратегия | Безопасность | Возможности | Задержка |
|---|---|---|---|
| Без ограничений | Наименьшая | Наивысшая | Наименьшая |
| Валидация ввода | Высокая | Средняя | Низкая |
| Фильтрация вывода | Высокая | Средняя | Низкая |
| Механизмы безопасности | Наивысшая | Наименьшая | Наивысшая |
| Соответствие требованиям | Наивысшая | Наименьшая | Наивысшая |
Связанные материалы
- Стратегии маршрутизации моделей — маршрутизация на основе возможностей, стоимости и задержки
- Оптимизация затрат для систем LLM — квотирование токенов, модели резервного копирования, кэширование
- Проектирование многомоделевых систем — архитектура для нескольких моделей
- Архитектура LLM — столп проектирования системы: маршрутизация, стоимость, ограничения и оркестрация