0Pricing
AI Agents · Lekcja

Egzekwowanie zasad dotyczących działań agentów

Sprawdzanie zasad przed działaniem, listy dozwolonych i zablokowanych oraz dynamiczne reguły zasad.

Egzekwowanie zasad dotyczących działań agentów to bezpłatna lekcja AI Agents na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Agents, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Agents zawiera 4 lekcji w sumie.

Czym jest egzekwowanie zasad agenta?

Egzekwowanie zasad to bramka działająca w czasie wykonywania, która uruchamia się przed każdym działaniem agenta i decyduje, czy dane działanie jest dozwolone. Bez niej jedynym ograniczeniem agenta jest stosowanie się LLM-a do instrukcji — które można obejść lub błędnie zinterpretować.

Egzekwowanie zasad musi odbywać się poza LLM-em, w infrastrukturze użytkownika.

Wzorzec sprawdzania przed wykonaniem działania

Przed wykonaniem dowolnego narzędzia należy wywołać can_agent_do(action, context). Ta funkcja jest jedynym punktem egzekwowania zasad — każda ścieżka prowadząca do wykonania działania musi przez nią przechodzić.

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)

Definiowanie list dozwolonych i zabronionych

Lista dozwolonych wylicza wszystkie działania, które agent może podjąć. Wszystko, czego nie ma na liście, jest domyślnie blokowane. Lista zabronionych stanowi dodatkową warstwę ochrony dla działań, na które nigdy nie należy zezwalać, niezależnie od kontekstu.

# 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}")

Sprawdzanie zasad zależnych od kontekstu

Poza prostymi listami dozwolonych i zabronionych zasady często zależą od kontekstu: kim jest użytkownik, jaka jest jego rola, która jest godzina i jaki zasób jest celem działania. Są to sprawdzenia zależne od kontekstu.

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))

Dynamiczne zasady z silnika zasad

Zasady zapisane na stałe w kodzie trudno aktualizować w środowisku produkcyjnym. Należy użyć silnika zasad (takiego jak OPA — Open Policy Agent) do oceniania zasad zdefiniowanych jako dane, a nie kod. Zasady można wtedy aktualizować bez ponownego wdrażania agenta.

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}'

Fail-closed a fail-open

Gdy silnik zasad jest niedostępny, dostępne są dwie możliwości:

  • Fail-closed: odmowa wszystkich działań. Jest to bezpieczne, ale agent przestaje działać.
  • Fail-open: zezwolenie na wszystkie działania. Agent nadal działa, ale zasady nie są egzekwowane.

W przypadku agentów wrażliwych pod względem bezpieczeństwa należy zawsze stosować fail-closed. W przypadku agentów produktywności wykonujących działania niskiego ryzyka fail-open może być dopuszczalne.

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)'

Ograniczanie częstotliwości działań

Egzekwowanie zasad może obejmować limity częstotliwości: agent może mieć możliwość wysyłania wiadomości e-mail, ale tylko 5 na sesję. Po przekroczeniu limitu działanie zostaje odrzucone.

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}')

Rejestrowanie naruszeń zasad

Każde odrzucenie działania przez zasady musi być rejestrowane wraz z pełnym kontekstem. Te dzienniki są pierwszym miejscem, które należy sprawdzić podczas debugowania nieoczekiwanego zachowania agenta lub badania incydentu bezpieczeństwa.

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

Ograniczanie zakresu zasobów

Nawet w przypadku dozwolonych działań należy ograniczyć zakres zasobów, do których agent ma dostęp. Agent obsługujący dokumenty użytkownika A nie powinien móc odczytywać plików użytkownika B, nawet jeśli read_file znajduje się na liście dozwolonych.

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))

Testowanie reguł zasad

Reguły zasad są kodem — muszą być testowane. Należy napisać testy jednostkowe dla każdej reguły, aby upewnić się, że odmowy i zezwolenia działają prawidłowo, a przypadki brzegowe nie powodują przypadkowego obejścia zasad.

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()

Buforowanie decyzji dotyczących zasad

Wywoływanie silnika zasad przy każdym działaniu zwiększa opóźnienie, szczególnie w przypadku korzystania z zewnętrznej usługi OPA. Należy buforować ostatnie decyzje dla par (action, context_hash) z krótkim TTL, aby ograniczyć liczbę komunikacji zwrotnych.

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

Na czym polega podejście fail-closed do egzekwowania zasad?

Decyzja między fail-closed a fail-open jest podstawowym kompromisem dotyczącym bezpieczeństwa w każdym systemie egzekwowania zasad. Wiedza o tym, kiedy właściwe jest każde z tych podejść, stanowi kluczowy element zarządzania.

Podsumowanie egzekwowania zasad agenta

Egzekwowanie zasad agenta obejmuje: sprawdzanie przed wykonaniem działania jako jedyną bramkę, listy dozwolonych i zabronionych do podejmowania podstawowych decyzji, reguły zależne od kontekstu do ograniczania zakresu zasobów i sprawdzania ról, dynamiczny silnik zasad (OPA) do obsługi aktualizowalnych reguł, limity częstotliwości dla każdego działania w ramach sesji oraz domyślne ustawienie fail-closed w przypadku agentów o krytycznym znaczeniu dla bezpieczeństwa.

Często zadawane pytania

Czy lekcja „Egzekwowanie zasad dotyczących działań agentów” jest bezpłatna?

Tak — pełny tekst „Egzekwowanie zasad dotyczących działań agentów” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Agents, przejdź na CoddyKit PRO. Kurs AI Agents zawiera 4 lekcji w sumie.

Co nauczysz się w „Egzekwowanie zasad dotyczących działań agentów”?

Sprawdzanie zasad przed działaniem, listy dozwolonych i zablokowanych oraz dynamiczne reguły zasad. Ćwiczysz AI Agents z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć AI Agents?

Nie wymagamy żadnego doświadczenia. AI Agents w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Egzekwowanie zasad dotyczących działań agentów”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji AI Agents?

Tak. Każda lekcja AI Agents zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Niezmienny rejestr działań agentów
  2. Egzekwowanie zasad dotyczących działań agentów
  3. Zgodność z regulacjami: GDPR i SOC2
  4. Bramki zatwierdzania z udziałem człowieka
← Powrót do AI Agents