Applicazione delle policy alle azioni degli agenti
Controlli delle policy prima dell’azione, allowlist/denylist e regole dinamiche.
Applicazione delle policy alle azioni degli agenti è una lezione AI Agents gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Agents, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Agents include 4 lezioni in totale.
Che cos'è l'applicazione delle policy dell'agente?
L'applicazione delle policy è il controllo in fase di esecuzione che viene effettuato prima di ogni azione dell'agente per stabilire se l'azione è consentita. In sua assenza, l'unico vincolo dell'agente è la capacità dell'LLM di seguire le istruzioni, che possono essere aggirate o interpretate erroneamente.
L'applicazione deve avvenire al di fuori dell'LLM, nella vostra infrastruttura.
Il pattern del controllo pre-azione
Prima di eseguire qualsiasi strumento, chiamare can_agent_do(action, context). Questa funzione è il singolo punto di controllo: ogni percorso che porta all'esecuzione di un'azione passa attraverso di essa.
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)Definizione di allowlist e denylist
La allowlist elenca ogni azione che l'agente è autorizzato a eseguire. Tutto ciò che non è nell'elenco viene bloccato per impostazione predefinita. La denylist aggiunge un ulteriore livello di sicurezza per le azioni che non devono mai essere consentite, indipendentemente dal contesto.
# 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}")
Controlli delle policy specifici per il contesto
Oltre ai semplici elenchi di azioni consentite o negate, le policy spesso dipendono dal contesto: chi è l'utente, qual è il suo ruolo, che ora è e qual è la risorsa di destinazione. Questi sono controlli specifici per il contesto.
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))
Policy dinamiche da un policy engine
Le policy hard-coded sono difficili da aggiornare in produzione. Utilizzare un policy engine, come OPA — Open Policy Agent, per valutare policy definite come dati e non come codice. Le policy possono essere aggiornate senza ridistribuire l'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}'Fail-closed e fail-open
Quando il policy engine non è disponibile, ci sono due opzioni:
- Fail-closed: negare tutte le azioni. È sicuro, ma l'agente smette di funzionare.
- Fail-open: consentire tutte le azioni. L'agente continua a funzionare, ma la policy non viene applicata.
Per gli agenti sensibili alla sicurezza, utilizzare sempre il fail-closed. Per gli agenti di produttività con azioni a basso rischio, il fail-open può essere accettabile.
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)'Limitazione della frequenza delle azioni
L'applicazione delle policy può includere limiti di frequenza: un agente può essere autorizzato a inviare email, ma solo 5 per sessione. Se il limite viene superato, l'azione viene negata.
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}')
Registrazione delle violazioni delle policy
Ogni diniego della policy deve essere registrato con il contesto completo. Questi log sono il primo luogo da consultare per eseguire il debug di un comportamento imprevisto dell'agente o per indagare su un incidente di sicurezza.
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, reasonDefinizione dell'ambito delle risorse
Anche per le azioni consentite, limitare l'ambito delle risorse a cui l'agente può accedere. Un agente che gestisce i documenti dell'utente A non dovrebbe poter leggere i file dell'utente B, anche se read_file è presente nell'allowlist.
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))
Test delle regole delle policy
Le regole delle policy sono codice e devono essere testate. Scrivere unit test per ogni regola, così da garantire il corretto funzionamento dei dinieghi e delle autorizzazioni ed evitare che i casi limite creino bypass accidentali delle policy.
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()Memorizzazione nella cache delle decisioni delle policy
Chiamare il policy engine per ogni singola azione aggiunge latenza, soprattutto quando si utilizza un servizio OPA esterno. Memorizzare nella cache le decisioni recenti per le coppie (action, context_hash) con un TTL breve per ridurre i round-trip.
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 resultQual è l'approccio fail-closed all'applicazione delle policy?
La scelta tra fail-closed e fail-open è un compromesso fondamentale per la sicurezza di qualsiasi sistema di applicazione delle policy. Sapere quando è appropriato ciascun approccio è un concetto essenziale di governance.
Riepilogo dell'applicazione delle policy
L'applicazione delle policy degli agenti utilizza: un controllo pre-azione come singolo punto di accesso, allowlist + denylist per le decisioni di base, regole specifiche per il contesto per limitare l'ambito delle risorse e verificare i ruoli, un policy engine dinamico (OPA) per regole aggiornabili, limiti di frequenza per azione e per sessione e impostazioni predefinite fail-closed per gli agenti critici per la sicurezza.
Domande Frequenti
La lezione «Applicazione delle policy alle azioni degli agenti» è gratuita?
Sì — il testo completo di «Applicazione delle policy alle azioni degli agenti» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Agents, passa a CoddyKit PRO. Il corso AI Agents include 4 lezioni in totale.
Cosa imparerò in «Applicazione delle policy alle azioni degli agenti»?
Controlli delle policy prima dell’azione, allowlist/denylist e regole dinamiche. Eserciti AI Agents con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare AI Agents?
Non è richiesta alcuna esperienza precedente. AI Agents su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.
Quanto tempo richiede la lezione «Applicazione delle policy alle azioni degli agenti»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione AI Agents?
Sì. Ogni lezione AI Agents include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Registrazione immutabile delle azioni degli agenti
- Applicazione delle policy alle azioni degli agenti
- Conformità normativa: GDPR e SOC2
- Gate di approvazione con supervisione umana