Guardrails para LLMs en la práctica: qué funciona realmente
Controla el riesgo, no solo el modelo.
Los modelos de lenguaje grande (LLM) son impredecibles. Alucinan, filtran datos, generan contenido dañino o rechazan solicitudes legítimas. Los mecanismos de protección (guardrails) restringen el comportamiento del modelo sin sacrificar su capacidad.
La clave está en saber qué mecanismos de protección son importantes y cuáles son solo ruido.
Los guardrails no se tratan de controlar el modelo. Se tratan de controlar el riesgo.

Validación de entrada
El guardrail más importante. Una mala entrada produce una mala salida, y una mala entrada también puede inyectar prompts en tu sistema.
Estrategia 1: Sanitización de Prompts
Sane los patrones peligrosos temprano:
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
Esto no es a prueba de balas. Las entradas adversarias son creativas. Pero captura las obvias, y las obvias son las más comunes.
Estrategia 2: Límites de longitud de entrada
Los límites de longitud previenen el desperdicio de tokens y los tiempos de espera:
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"Entrada demasiado larga: {len(prompt)} > {self.max_length}"
return True, "OK"
Estrategia 3: Filtrado de contenido
El filtrado de contenido bloquea las violaciones de políticas. Los patrones aquí dependen de tu dominio:
class ContentFilter:
def __init__(self):
self.blocked_topics = [
"violencia", "discurso de odio", "autolesiones",
"contenido sexual", "actividades ilegales",
]
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"Bloqueado: {topic}"
return True, "OK"
La coincidencia de cadenas simple es rápida pero imprecisa. Para producción, utiliza un modelo clasificador —incluso uno pequeño como qwen3-1.7b— para detectar violaciones de políticas. Es más preciso y más difícil de evadir.
Filtrado de salida
La salida del modelo también necesita verificación. Estructura, contenido y hechos.
Estrategia 1: Validación de respuesta
Valida la estructura primero. Si esperas JSON, verifica si es 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"Campo faltante: {field}"
return True, "OK"
Estrategia 2: Filtrado de contenido
Filtra el contenido dañino:
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"
Estrategia 3: Verificación de hechos
La verificación de hechos es más difícil. No puedes validar cada afirmación, así que elige las que importan:
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"
Para una verificación de hechos real, necesitas una tubería de recuperación (retrieval pipeline). Comprueba las afirmaciones contra una base de conocimientos, no contra un diccionario codificado.
Mecanismos de seguridad
Estrategia 1: Limitación de tasa
La limitación de tasa previene el abuso:
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
Estrategia 2: Presupuesto de tokens
El presupuesto de tokens limita los costos por solicitud:
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"
Estrategia 3: Gestión de la ventana de contexto
La gestión de la ventana de contexto previene el desbordamiento:
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)
El recorte de ventana deslizante es simple pero pierde el contexto inicial. Enfoques mejores utilizan resumen o compresión basada en atención, pero eso añade latencia.
Cumplimiento normativo
Los sistemas empresariales necesitan guardrails de cumplimiento. Dos que más importan:
Patrón 1: Residencia de datos
Residencia de datos — asegúrate de que los datos permanezcan dentro de los límites geográficos requeridos:
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"
Patrón 2: Registro de auditoría
Registro de auditoría — registra todas las interacciones del modelo:
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")
Los registros de auditoría son críticos para la depuración y el cumplimiento. Hazlos estructurados, solo para agregar (append-only) y almacenados de forma segura.
Poniéndolo todo junto
Patrón 1: Guardrails simples
Una tubería de guardrails simple:
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
Patrón 2: Guardrails avanzados
Los guardrails avanzados añaden sanitización, limitación de tasa y presupuestos de tokens:
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
Cuando importan los guardrails
Los guardrails importan cuando estás construyendo sistemas orientados al usuario, manejando datos sensibles o ejecutándose en producción. También importan cuando tienes requisitos de cumplimiento — GDPR, HIPAA, SOC 2.
No importan cuando estás prototipando, utilizando modelos solo para herramientas internas o no manejando datos sensibles. Omítelos hasta que los necesites.
La compensación siempre es capacidad versus seguridad. Más guardrails significan menos fallos, pero también menos capacidades. Encuentra el equilibrio que funcione para tu sistema.
Compensaciones
| Estrategia | Seguridad | Capacidad | Latencia |
|---|---|---|---|
| Sin guardrails | Más baja | Más alta | Más baja |
| Validación de entrada | Alta | Media | Baja |
| Filtrado de salida | Alta | Media | Baja |
| Mecanismos de seguridad | Más alta | Más baja | Más alta |
| Cumplimiento | Más alta | Más baja | Más alta |
Relacionado
- Estrategias de enrutamiento de modelos — enrutamiento basado en capacidad, consciente del costo y de la latencia
- Optimización de costos para sistemas LLM — presupuesto de tokens, modelos de respaldo, almacenamiento en caché
- Diseño de sistemas de múltiples modelos — arquitectura para múltiples modelos
- Arquitectura de LLM — pilar de diseño de sistemas: enrutamiento, costos, guardrails y orquestación