0Pricing
AI Agents · Lección

Aplicación de políticas a las acciones de agentes

Comprobaciones de políticas antes de cada acción, listas de permitidos y bloqueados, y reglas de políticas dinámicas.

Aplicación de políticas a las acciones de agentes es una lección gratuita de AI Agents en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Agents, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Agents incluye 4 lecciones en total.

¿Qué es la aplicación de políticas del agente?

La aplicación de políticas es el mecanismo de control en tiempo de ejecución que se ejecuta antes de cada acción del agente para decidir si está permitida. Sin ella, la única limitación del agente es el seguimiento de instrucciones del LLM, que se puede eludir o interpretar incorrectamente.

La aplicación debe estar fuera del LLM, en su infraestructura.

El patrón de comprobación previa a la acción

Antes de ejecutar cualquier herramienta, llame a can_agent_do(action, context). Esta función es el único punto de aplicación: todas las rutas de ejecución de acciones pasan por ella.

def can_agent_do(action: str, context: dict) -> tuple[bool, str]:
    '''
    Returns (allowed: bool, reason: str).
    Context includes: user_id, agent_id, session_id, parameters, timestamp.
    '''
    # 1. Check denylist first (fast path for obvious violations)
    if action in DENIED_ACTIONS:
        return False, f'Action "{action}" is on the global denylist'

    # 2. Check allowlist
    if action not in ALLOWED_ACTIONS:
        return False, f'Action "{action}" is not on the allowlist'

    # 3. Context-specific checks
    return check_context_policy(action, context)

Definición de listas de permitidos y bloqueados

La lista de permitidos enumera todas las acciones que el agente puede realizar. Cualquier acción que no figure en la lista se bloquea de forma predeterminada. La lista de bloqueados añade una capa de seguridad para las acciones que nunca deberían permitirse, independientemente del contexto.

# Allowlist: tools the agent can use
ALLOWED_ACTIONS = {
    'web_search',
    'read_file',
    'write_file',
    'send_email',
    'create_calendar_event',
    'query_database',
    'execute_python_sandbox',
    'fetch_url',
    'create_ticket'
}

# Denylist: actions that are always blocked, regardless of context
DENIED_ACTIONS = {
    'delete_all_records',
    'export_entire_database',
    'send_mass_email',
    'modify_system_config',
    'create_admin_user',
    'disable_audit_logging'
}

if __name__ == '__main__':
    for action in ('web_search', 'send_mass_email'):
        print(f"{action}: allowed={action in ALLOWED_ACTIONS} denied={action in DENIED_ACTIONS}")

Comprobaciones de políticas específicas del contexto

Además de las listas sencillas de permitidos y bloqueados, las políticas suelen depender del contexto: ¿quién es el usuario?, ¿qué función desempeña?, ¿qué hora es?, ¿cuál es el recurso de destino? Estas son comprobaciones específicas del contexto.

from datetime import datetime, timezone

def check_context_policy(action: str, context: dict) -> tuple[bool, str]:
    user_id   = context.get('user_id', '')
    params    = context.get('parameters', {})
    user_role = context.get('user_role', 'user')

    # send_email: only agents with email_sender role
    if action == 'send_email':
        if user_role not in ('email_agent', 'admin'):
            return False, f'Role "{user_role}" cannot send emails'
        recipient = params.get('to', '')
        if not recipient.endswith('@trusted-domain.com'):
            return False, 'Email recipient must be in @trusted-domain.com'

    # write_file: path restrictions
    if action == 'write_file':
        path = params.get('path', '')
        if not path.startswith('/tmp/') and not path.startswith('/workspace/'):
            return False, f'File writes outside /tmp/ and /workspace/ are not allowed'

    return True, 'Permitted'

if __name__ == '__main__':
    ctx = {'user_id': 'u1', 'user_role': 'user',
           'parameters': {'to': 'someone@gmail.com'}}
    print('send_email as user:', check_context_policy('send_email', ctx))

    ctx2 = {'user_id': 'u1', 'user_role': 'user',
            'parameters': {'path': '/etc/passwd'}}
    print('write_file outside sandbox:', check_context_policy('write_file', ctx2))

Políticas dinámicas desde un motor de políticas

Las políticas codificadas directamente son difíciles de actualizar en producción. Utilice un motor de políticas (como OPA — Open Policy Agent) para evaluar políticas definidas como datos, no como código. Las políticas se pueden actualizar sin volver a desplegar el agente.

import requests

OPA_URL = 'http://localhost:8181/v1/data/agent/allow'

def opa_policy_check(action: str, context: dict) -> tuple[bool, str]:
    payload = {
        'input': {
            'action':   action,
            'user_id':  context.get('user_id'),
            'role':     context.get('user_role', 'user'),
            'params':   context.get('parameters', {}),
            'time_utc': datetime.now(timezone.utc).isoformat()
        }
    }
    try:
        resp = requests.post(OPA_URL, json=payload, timeout=0.5)
        result = resp.json().get('result', {})
        allowed = result.get('allow', False)
        reason  = result.get('reason', 'Policy decision')
        return allowed, reason
    except Exception as e:
        # Fail closed: deny if policy engine is unreachable
        return False, f'Policy engine unavailable: {e}'

Fallo cerrado frente a fallo abierto

Cuando el motor de políticas no está disponible, tiene dos opciones:

  • Fail-closed: denegar todas las acciones. Es seguro, pero el agente deja de funcionar.
  • Fail-open: permitir todas las acciones. El agente sigue funcionando, pero la política no se aplica.

En agentes sensibles a la seguridad, utilice siempre fail-closed. En agentes de productividad con acciones de bajo riesgo, fail-open puede ser aceptable.

FAIL_CLOSED = True  # Configure per agent

def safe_policy_check(action: str, context: dict) -> tuple[bool, str]:
    try:
        return can_agent_do(action, context)
    except Exception as e:
        if FAIL_CLOSED:
            return False, f'Policy check failed (fail-closed): {e}'
        else:
            # Log the failure but allow the action
            import logging
            logging.warning('Policy check error (fail-open): %s', e)
            return True, 'Policy check bypassed due to error (fail-open)'

Limitación de la tasa de acciones

La aplicación de políticas puede incluir límites de tasa: se puede permitir que un agente envíe correos electrónicos, pero solo 5 por sesión. Si supera el límite, la acción se deniega.

from collections import defaultdict
import time

action_counts: dict[str, dict[str, int]] = defaultdict(lambda: defaultdict(int))
action_window_start: dict[str, float] = defaultdict(float)

ACTION_RATE_LIMITS = {
    'send_email':   {'limit': 5,  'window_secs': 3600},  # 5/hour
    'write_file':   {'limit': 50, 'window_secs': 300},
    'web_search':   {'limit': 20, 'window_secs': 60}
}

def check_rate_limit(action: str, session_id: str) -> tuple[bool, str]:
    limit_config = ACTION_RATE_LIMITS.get(action)
    if not limit_config:
        return True, 'No rate limit defined'

    window = limit_config['window_secs']
    now = time.time()
    key = f'{session_id}:{action}'

    if now - action_window_start[key] > window:
        action_counts[key] = defaultdict(int)
        action_window_start[key] = now

    action_counts[key]['count'] += 1
    if action_counts[key]['count'] > limit_config['limit']:
        return False, f'Rate limit exceeded: {action} ({limit_config["limit"]}/{window}s)'
    return True, 'Within rate limit'

if __name__ == '__main__':
    for i in range(6):
        allowed, reason = check_rate_limit('send_email', 'session-1')
    print(f'After 6 send_email calls: allowed={allowed}, reason={reason}')

Registro de infracciones de políticas

Cada denegación de una política debe registrarse con todo el contexto. Estos registros son el primer lugar que se debe consultar al depurar un comportamiento inesperado del agente o investigar un incidente de seguridad.

import logging, json, time

policy_logger = logging.getLogger('agent.policy')

def enforced_action(agent_id: str, user_id: str, action: str,
                    context: dict, audit_log) -> tuple[bool, str]:
    allowed, reason = safe_policy_check(action, context)

    log_entry = {
        'ts':       time.time(),
        'agent_id': agent_id,
        'user_id':  user_id,
        'action':   action,
        'allowed':  allowed,
        'reason':   reason,
        'params':   context.get('parameters', {})
    }

    if allowed:
        policy_logger.info('ALLOWED %s', json.dumps(log_entry))
    else:
        policy_logger.warning('DENIED %s', json.dumps(log_entry))

    audit_log.append(
        agent_id, user_id,
        f'POLICY_{"ALLOW" if allowed else "DENY"}',
        {'action': action},
        {'allowed': allowed, 'reason': reason},
        context.get('session_id', '')
    )
    return allowed, reason

Delimitación del acceso a recursos

Incluso para las acciones permitidas, limite los recursos a los que puede acceder el agente. Un agente que gestiona los documentos del usuario A no debería poder leer los archivos del usuario B, aunque read_file figure en la lista de permitidos.

def check_resource_scope(action: str, context: dict) -> tuple[bool, str]:
    user_id  = context.get('user_id', '')
    params   = context.get('parameters', {})

    if action == 'read_file':
        path = params.get('path', '')
        # Each user's files must be under their own namespace
        if not path.startswith(f'/workspace/{user_id}/'):
            return False, (
                f'User {user_id} cannot read files outside '
                f'/workspace/{user_id}/'
            )

    if action == 'query_database':
        table = params.get('table', '')
        allowed_tables = {'products', 'public_docs', f'user_{user_id}_data'}
        if table not in allowed_tables:
            return False, f'Table "{table}" not in scope for user {user_id}'

    return True, 'Resource scope check passed'

if __name__ == '__main__':
    ctx = {'user_id': 'u1', 'parameters': {'path': '/workspace/u2/secret.txt'}}
    print('Cross-user file read:', check_resource_scope('read_file', ctx))

    ctx2 = {'user_id': 'u1', 'parameters': {'path': '/workspace/u1/notes.txt'}}
    print('Own file read:      ', check_resource_scope('read_file', ctx2))

Pruebas de las reglas de políticas

Las reglas de políticas son código y deben probarse. Escriba pruebas unitarias para cada regla a fin de garantizar que las denegaciones y autorizaciones funcionan correctamente y que los casos límite no crean bypasses accidentales de las políticas.

def test_policy_rules():
    # Denylist blocks unconditionally
    ok, msg = can_agent_do('delete_all_records', {'user_role': 'admin'})
    assert not ok, 'Denylist should block even for admin'

    # Email requires trusted domain
    ok, msg = can_agent_do('send_email', {
        'user_role': 'email_agent',
        'parameters': {'to': 'attacker@evil.com'}
    })
    assert not ok, 'Should block untrusted email recipient'

    # File write outside allowed paths
    ok, msg = can_agent_do('write_file', {
        'user_role': 'user',
        'parameters': {'path': '/etc/crontab'}
    })
    assert not ok, 'Should block write to /etc/'

    print('All policy tests passed')

test_policy_rules()

Almacenamiento en caché de las decisiones de políticas

Llamar al motor de políticas para cada acción añade latencia, especialmente cuando se utiliza un servicio OPA externo. Almacene en caché durante un TTL corto las decisiones recientes correspondientes a pares (action, context_hash) para reducir los viajes de ida y vuelta.

import hashlib, time

policy_cache: dict[str, dict] = {}
POLICY_CACHE_TTL = 10  # seconds — short TTL so policy updates take effect quickly

def cached_policy_check(action: str, context: dict) -> tuple[bool, str]:
    ctx_hash = hashlib.md5(
        f'{action}:{context.get("user_id")}:{context.get("user_role")}'
        .encode()
    ).hexdigest()
    key = f'{action}:{ctx_hash}'
    entry = policy_cache.get(key)
    if entry and time.time() - entry['ts'] < POLICY_CACHE_TTL:
        return entry['result']
    result = can_agent_do(action, context)
    policy_cache[key] = {'result': result, 'ts': time.time()}
    return result

¿Qué es el enfoque «fail-closed» de aplicación de políticas?

La decisión entre fail-closed y fail-open es una disyuntiva de seguridad fundamental en cualquier sistema de aplicación de políticas. Saber cuándo es adecuado cada enfoque constituye un concepto esencial de gobernanza.

Resumen de la aplicación de políticas

La aplicación de políticas de agentes utiliza: una comprobación previa a la acción como único punto de control, listas de permitidos + listas de bloqueados para las decisiones básicas, reglas específicas del contexto para delimitar recursos y comprobar funciones, un motor de políticas dinámico (OPA) para reglas actualizables, límites de tasa por acción y sesión, y valores predeterminados de tipo fail-closed para agentes críticos desde el punto de vista de la seguridad.

Preguntas frecuentes

¿La lección «Aplicación de políticas a las acciones de agentes» es gratis?

Sí — el texto completo de «Aplicación de políticas a las acciones de agentes» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Agents, actualiza a CoddyKit PRO. El curso de AI Agents incluye 4 lecciones en total.

¿Qué aprenderé en «Aplicación de políticas a las acciones de agentes»?

Comprobaciones de políticas antes de cada acción, listas de permitidos y bloqueados, y reglas de políticas dinámicas. Practicas AI Agents con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar AI Agents?

No se requiere experiencia previa. AI Agents en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.

¿Cuánto tiempo toma la lección «Aplicación de políticas a las acciones de agentes»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de AI Agents?

Sí. Cada lección de AI Agents incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Registro inmutable de acciones de agentes
  2. Aplicación de políticas a las acciones de agentes
  3. Cumplimiento normativo: GDPR y SOC2
  4. Puntos de aprobación con supervisión humana
← Volver a AI Agents