Guardrails para LLMs en la práctica: qué funciona realmente

Controla el riesgo, no solo el modelo.

Índice

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.

Guardrails de LLM en la práctica

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

Suscribirse

Recibe nuevas publicaciones sobre sistemas, infraestructura e ingeniería de IA.