فرض السياسات على إجراءات الوكيل
فحوصات السياسة قبل الإجراء، وقوائم السماح والمنع، وقواعد السياسة الديناميكية.
فرض السياسات على إجراءات الوكيل درس مجاني في AI Agents على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Agents، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Agents 4 دروس في المجموع.
ما إنفاذ سياسات الوكيل؟
إنفاذ السياسات هو بوابة وقت التشغيل التي تعمل قبل كل إجراء للوكيل لتقرر ما إذا كان الإجراء مسموحًا به. ومن دونها، يكون القيد الوحيد على الوكيل هو اتباع التعليمات الواردة من LLM — وهو ما يمكن تجاوزه أو تفسيره على نحو خاطئ.
يجب أن يحدث الإنفاذ خارج LLM، ضمن بنيتك التحتية.
نمط الفحص السابق للإجراء
قبل تنفيذ أي أداة، استدعِ can_agent_do(action, context). هذه الدالة هي نقطة الإنفاذ الوحيدة — إذ يمر كل مسار يؤدي إلى تنفيذ إجراء عبرها.
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)تعريف قوائم السماح والمنع
تسرد قائمة السماح كل إجراء يُسمح للوكيل بتنفيذه. ويُحظر افتراضيًا أي إجراء غير موجود في القائمة. وتضيف قائمة المنع طبقة أمان إضافية للإجراءات التي ينبغي عدم السماح بها مطلقًا، بصرف النظر عن السياق.
# 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}")
فحوصات السياسات الخاصة بالسياق
إضافةً إلى قوائم السماح والمنع البسيطة، تعتمد السياسات غالبًا على السياق: من هو المستخدم، وما دوره، وما الوقت، وما المورد المستهدف؟ هذه هي الفحوصات الخاصة بالسياق.
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))
السياسات الديناميكية من محرك السياسات
يصعب تحديث السياسات المضمّنة في الكود داخل بيئة التشغيل الفعلية. استخدم محرك سياسات (مثل OPA — Open Policy Agent) لتقييم السياسات المعرّفة باعتبارها بيانات لا كودًا. ويمكن تحديث السياسات من دون إعادة نشر الوكيل.
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 = 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)'تحديد معدل الإجراءات
يمكن أن يتضمن إنفاذ السياسات حدودًا لمعدل التنفيذ: فقد يُسمح للوكيل بإرسال رسائل بريد إلكتروني، ولكن بحد أقصى 5 رسائل لكل جلسة. عند تجاوز الحد، يُرفض الإجراء.
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}')
تسجيل انتهاكات السياسات
يجب تسجيل كل رفض لسياسة مع السياق الكامل. وهذه السجلات هي أول ما ينبغي فحصه عند تصحيح سلوك غير متوقع للوكيل أو التحقيق في حادث أمني.
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تحديد نطاق الموارد
حتى بالنسبة إلى الإجراءات المسموح بها، حدّد نطاق الموارد التي يمكن للوكيل الوصول إليها. فلا ينبغي لوكيل يتعامل مع مستندات المستخدم A أن يتمكن من قراءة ملفات المستخدم B، حتى إذا كانت read_file موجودة في قائمة السماح.
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))
اختبار قواعد السياسات
قواعد السياسات هي كود — ولذلك يجب اختبارها. اكتب اختبارات وحدات لكل قاعدة لضمان عمل حالات الرفض والسماح على نحو صحيح، ولمنع الحالات الحدية من إنشاء تجاوزات غير مقصودة للسياسة.
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()تخزين قرارات السياسات مؤقتًا
يؤدي استدعاء محرك السياسات عند كل إجراء على حدة إلى إضافة زمن استجابة، ولا سيما عند استخدام خدمة OPA خارجية. خزّن القرارات الحديثة مؤقتًا لأزواج (action, context_hash) مع TTL قصير لتقليل عمليات ذهاب الطلب وإيابه.
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ما نهج «الفشل بالمنع» في إنفاذ السياسات؟
يُعد القرار بين الفشل بالمنع والفشل بالسماح مفاضلة أمنية أساسية في أي نظام لإنفاذ السياسات. ومعرفة متى يكون كل نهج مناسبًا مفهومًا جوهريًا في الحوكمة.
مراجعة إنفاذ سياسات الوكيل
يستخدم إنفاذ سياسات الوكيل ما يلي: فحصًا سابقًا للإجراء باعتباره البوابة الوحيدة، وقوائم السماح والمنع لاتخاذ القرارات الأساسية، وقواعد خاصة بالسياق لتحديد نطاق الموارد والتحقق من الأدوار، ومحرك سياسات ديناميكيًا (OPA) للقواعد القابلة للتحديث، وحدودًا لمعدل كل إجراء في كل جلسة، وافتراض الفشل بالمنع للوكلاء الحرجين أمنيًا.
الأسئلة الشائعة
هل درس «فرض السياسات على إجراءات الوكيل» مجاني؟
نعم — نص درس «فرض السياسات على إجراءات الوكيل» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Agents، انتقل إلى CoddyKit PRO. تتضمن دورة AI Agents 4 دروس في المجموع.
ماذا ستتعلم في «فرض السياسات على إجراءات الوكيل»؟
فحوصات السياسة قبل الإجراء، وقوائم السماح والمنع، وقواعد السياسة الديناميكية. تتمرن على AI Agents مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ AI Agents؟
لا تُشترط خبرة سابقة. AI Agents على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «فرض السياسات على إجراءات الوكيل»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس AI Agents هذا؟
نعم. كل درس في AI Agents يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- تسجيل الإجراءات غير القابل للتغيير للوكلاء
- فرض السياسات على إجراءات الوكيل
- الامتثال التنظيمي: GDPR وSOC2
- بوابات الموافقة بوجود الإنسان ضمن الحلقة