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

Валидация ввода
Самое важное ограничение. Плохой ввод приводит к плохому выводу, а также может спровоцировать инъекцию промпта в вашу систему.
Стратегия 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"
Простое сопоставление строк работает быстро, но неточно. Для продакшена используйте модель-классификатор — даже небольшую, такую как Qwen2.5-1.5B — для выявления нарушений политик. Это более точно и сложнее для обхода.
Фильтрация вывода
Вывод модели также нуждается в проверке. Структура, контент и факты.
Стратегия 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)
Обрезка по скользящему окну проста, но приводит к потере раннего контекста. Более продвинутые подходы используют суммаризацию или сжатие на основе внимания (attention-based compression), но они добавляют задержку.
Соответствие требованиям (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 — столп системы дизайна: маршрутизация, стоимость, ограничители и оркестрация